<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="Java面试题（并发篇）, Angus">
    <meta name="description" content="Java面试并发题型讲解">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="referrer" content="no-referrer-when-downgrade">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    

    <title>Java面试题（并发篇） | Angus</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery-3.6.0.min.js"></script>

<meta name="generator" content="Hexo 6.1.0">
<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
<link rel="alternate" href="/atom.xml" title="Angus" type="application/atom+xml">
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>


<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">Angus</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>留言板</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">Angus</div>
        <div class="logo-desc">
            
            never give up!
            
        </div>
    </div>

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			留言板
		</a>
          
        </li>
        
        
        <li><div class="divider"></div></li>
        <li>
            <a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" class="waves-effect waves-light" target="_blank">
                <i class="fab fa-github-square fa-fw"></i>Fork Me
            </a>
        </li>
        
    </ul>
</div>


        </div>

        
            <style>
    .nav-transparent .github-corner {
        display: none !important;
    }

    .github-corner {
        position: absolute;
        z-index: 10;
        top: 0;
        right: 0;
        border: 0;
        transform: scale(1.1);
    }

    .github-corner svg {
        color: #0f9d58;
        fill: #fff;
        height: 64px;
        width: 64px;
    }

    .github-corner:hover .octo-arm {
        animation: a 0.56s ease-in-out;
    }

    .github-corner .octo-arm {
        animation: none;
    }

    @keyframes a {
        0%,
        to {
            transform: rotate(0);
        }
        20%,
        60% {
            transform: rotate(-25deg);
        }
        40%,
        80% {
            transform: rotate(10deg);
        }
    }
</style>

<a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" class="github-corner tooltipped hide-on-med-and-down" target="_blank"
   data-tooltip="Fork Me" data-position="left" data-delay="50">
    <svg viewBox="0 0 250 250" aria-hidden="true">
        <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
        <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
              fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
        <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
              fill="currentColor" class="octo-body"></path>
    </svg>
</a>
        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/22.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">Java面试题（并发篇）</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/Java/">
                                <span class="chip bg-color">Java</span>
                            </a>
                        
                            <a href="/tags/%E9%9D%A2%E8%AF%95/">
                                <span class="chip bg-color">面试</span>
                            </a>
                        
                            <a href="/tags/%E5%B9%B6%E5%8F%91/">
                                <span class="chip bg-color">并发</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/%E9%9D%A2%E8%AF%95%E5%AE%9D%E5%85%B8/" class="post-category">
                                面试宝典
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2022-07-29
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>更新日期:&nbsp;&nbsp;
                    2022-09-08
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    21.6k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    77 分
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        

        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h1 id="Java面试题（并发篇）"><a href="#Java面试题（并发篇）" class="headerlink" title="Java面试题（并发篇）"></a>Java面试题（并发篇）</h1><h2 id="一、线程相关"><a href="#一、线程相关" class="headerlink" title="一、线程相关"></a>一、线程相关</h2><h3 id="1、线程的⽣命周期？线程有⼏种状态"><a href="#1、线程的⽣命周期？线程有⼏种状态" class="headerlink" title="1、线程的⽣命周期？线程有⼏种状态"></a>1、线程的⽣命周期？线程有⼏种状态</h3><p><strong>线程通常有五种状态，创建，就绪，运⾏、阻塞和死亡状态：</strong> </p>
<ol>
<li><p>新建状态（New）：新创建了⼀个线程对象。 </p>
</li>
<li><p>就绪状态（Runnable）：线程对象创建后，其他线程调⽤了该对象的start⽅法。该状态的线程位于</p>
</li>
</ol>
<p>​	可运⾏线程池中，变得可运⾏，等待获取CPU的使⽤权。 </p>
<ol start="3">
<li><p>运⾏状态（Running）：就绪状态的线程获取了CPU，执⾏程序代码。 </p>
</li>
<li><p>阻塞状态（Blocked）：阻塞状态是线程因为某种原因放弃CPU使⽤权，暂时停⽌运⾏。直到线程进⼊就绪状态，才有机会转到运⾏状态。 </p>
</li>
<li><p>死亡状态（Dead）：线程执⾏完了或者因异常退出了run⽅法，该线程结束⽣命周期。</p>
</li>
</ol>
<p><strong>阻塞的情况⼜分为三种：</strong> </p>
<ol>
<li><p>等待阻塞：运⾏的线程执⾏wait⽅法，该线程会释放占⽤的所有资源，JVM会把该线程放⼊“等待 池”中。进⼊这个状态后，是不能⾃动唤醒的，必须依靠其他线程调⽤notify或notifyAll⽅法才能被 唤醒，wait是object类的⽅法 </p>
</li>
<li><p>同步阻塞：运⾏的线程在获取对象的同步锁时，若该同步锁被别的线程占⽤，则JVM会把该线程放 ⼊“锁池”中。 </p>
</li>
<li><p>其他阻塞：运⾏的线程执⾏sleep或join⽅法，或者发出了I/O请求时，JVM会把该线程置为阻塞状 态。当sleep状态超时、join等待线程终⽌或者超时、或者I/O处理完毕时，线程重新转⼊就绪状 态。sleep是Thread类的⽅法</p>
</li>
</ol>
<h3 id="2、线程基本方法"><a href="#2、线程基本方法" class="headerlink" title="2、线程基本方法"></a>2、线程基本方法</h3><ul>
<li>线程等待wait()：使一个线程处于等待（阻塞）状态，并且释放所持有的对象的锁；只有等待另外线程的通知或被中断才会返回，wait 方法一般用在同步方法或同步代码块中</li>
<li>线程睡眠sleep()：使一个正在运行的线程处于睡眠状态，但不会释放当前占有的锁。是一个静态方法，调用此方法要处理InterruptedException 异常；</li>
<li>等待其他线程终止 join()：线程进入阻塞状态，马上释放cpu的执行权，但依然会保留cpu的执行资格，所以有可能cpu下次进行线程调度还会让这个线程获取到执行权继续执行</li>
<li>线程让步yield()：使当前线程让出 CPU 执行时间片，与其他线程一起重新竞争 CPU 时间片，执行后线程进入阻塞状态，例如在线程B种调用线程A的join()，那线程B会进入到阻塞队列，直到线程A结束或中断线程。</li>
<li>线程唤醒notify()：唤醒一个处于等待状态的线程，当然在调用此方法的时候，并不能确切的唤醒某一个等待状态的线程，而是由 JVM 确定唤醒哪个线程，而且与优先级无关；</li>
<li>线程全部唤醒notityAll()：唤醒所有处于等待状态的线程，该方法并不是将对象的锁给所有线程，而是让它们竞争，只有获得锁的线程才能进入就绪状态；</li>
</ul>
<h3 id="2、sleep-、wait-、join-、yield-之间的的区别"><a href="#2、sleep-、wait-、join-、yield-之间的的区别" class="headerlink" title="2、sleep()、wait()、join()、yield()之间的的区别"></a>2、sleep()、wait()、join()、yield()之间的的区别</h3><p><strong>锁池：</strong>所有需要竞争同步锁的线程都会放在锁池当中，⽐如当前对象的锁已经被其中⼀个线程得到，则 </p>
<p>其他线程需要在这个锁池进⾏等待，当前⾯的线程释放同步锁后锁池中的线程去竞争同步锁，当某个线 </p>
<p>程得到后会进⼊就绪队列进⾏等待cpu资源分配。 </p>
<p><strong>等待池：</strong>当我们调⽤wait（）⽅法后，线程会放到等待池当中，等待池的线程是不会去竞争同步锁。只 </p>
<p>有调⽤了notify（）或notifyAll()后等待池的线程才会开始去竞争锁，notify（）是随机从等待池选出⼀ </p>
<p>个线程放到锁池，⽽notifyAll()是将等待池的所有线程放到锁池当中 </p>
<ol>
<li><p>sleep 是 Thread 类的静态本地⽅法，wait 则是 Object 类的本地⽅法。 </p>
</li>
<li><p>sleep⽅法不会释放lock，但是wait会释放，⽽且会加⼊到等待队列中。 </p>
</li>
<li><p>sleep⽅法不依赖于同步器synchronized，但是wait需要依赖synchronized关键字。 </p>
</li>
<li><p>sleep不需要被唤醒（休眠之后推出阻塞），但是wait需要（不指定时间需要被别⼈中断）。 </p>
</li>
<li><p>sleep ⼀般⽤于当前线程休眠，或者轮循暂停操作，wait 则多⽤于多线程之间的通信。 </p>
</li>
<li><p>sleep 会让出 CPU 执⾏时间且强制上下⽂切换，⽽ wait 则不⼀定，wait 后可能还是有机会重新竞 争到锁继续执⾏的。 </p>
</li>
<li><p>yield（）执⾏后线程直接进⼊就绪状态，⻢上释放了cpu的执⾏权，但是依然保留了cpu的执⾏资 格，所以有可能cpu下次进⾏线程调度还会让这个线程获取到执⾏权继续执⾏ </p>
</li>
<li><p>join（）执⾏后线程进⼊阻塞状态，例如在线程B中调⽤线程A的join（），那线程B会进⼊到阻塞队 列，直到线程A结束或中断线程</p>
</li>
</ol>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> InterruptedException <span class="token punctuation">{</span>
        Thread t1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Runnable</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token annotation punctuation">@Override</span>
            <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">try</span> <span class="token punctuation">{</span>
                    Thread<span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"22222222"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        t1<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        t1<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 这⾏代码必须要等t1全部执⾏完毕，才会执⾏</span>
        System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"1111"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
控制台输出：
<span class="token number">22222222</span> 
<span class="token number">1111</span>
</code></pre>
<h3 id="3、对线程安全的理解"><a href="#3、对线程安全的理解" class="headerlink" title="3、对线程安全的理解"></a>3、对线程安全的理解</h3><ul>
<li><p>不是线程安全、应该是内存安全，堆是共享内存，可以被所有线程访问，当多个线程访问⼀个对象时， </p>
<p>如果不⽤进⾏额外的同步控制或其他的协调操作，调⽤这个对象的⾏为都可以获得正确的结果，我们就 </p>
<p>说这个对象是线程安全的。 </p>
</li>
<li><p><strong>堆</strong>是进程和线程共有的空间，分全局堆和局部堆。全局堆就是所有没有分配的空间，局部堆就是⽤户分 </p>
<p>配的空间。堆在操作系统对进程初始化的时候分配，运⾏过程中也可以向系统要额外的堆，但是⽤完了 </p>
<p>要还给操作系统，要不然就是内存泄漏。在Java中，堆是Java虚拟机所管理的内存中最⼤的⼀块，是所有线程共享的⼀块内存区域，在虚拟机启动时创建。堆所存在的内存区域的唯⼀⽬的就是存放对象实 例，⼏乎所有的对象实例以及数组都在这⾥分配内存。 </p>
</li>
<li><p><strong>栈</strong>是每个线程独有的，保存其运⾏状态和局部⾃动变量的。栈在线程开始的时候初始化，每个线程的栈 </p>
<p>互相独⽴，因此，栈是线程安全的。操作系统在切换线程的时候会⾃动切换栈。栈空间不需要在⾼级语 </p>
<p>⾔⾥⾯显式的分配和释放。 </p>
</li>
<li><p>⽬前主流操作系统都是多任务的，即多个进程同时运⾏。为了保证安全，每个进程只能访问分配给⾃⼰ </p>
<p>的内存空间，⽽不能访问别的进程的，这是由操作系统保障的。 </p>
</li>
<li><p>在每个进程的内存空间中都会有⼀块特殊的公共区域，通常称为堆（内存）。进程内的所有线程都可以 </p>
<p>访问到该区域，这就是造成问题的潜在原因。</p>
</li>
</ul>
<h3 id="4、Thread和Runable的区别"><a href="#4、Thread和Runable的区别" class="headerlink" title="4、Thread和Runable的区别"></a>4、Thread和Runable的区别</h3><p>Thread和Runnable的实质是继承关系，没有可⽐性。⽆论使⽤Runnable还是Thread，都会new </p>
<p>Thread，然后执⾏run⽅法。⽤法上，如果有复杂的线程操作需求，那就选择继承Thread，如果只是简 </p>
<p>单的执⾏⼀个任务，那就实现runnable。</p>
<pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">//会卖出多⼀倍的票</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
        <span class="token comment" spellcheck="true">// TODO Auto-generated method stub </span>
        <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">MyThread</span> <span class="token keyword">extends</span> <span class="token class-name">Thread</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> ticket <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Thread ticket = "</span> <span class="token operator">+</span> ticket<span class="token operator">--</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>ticket <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token keyword">break</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">//正常卖出</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test2</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment" spellcheck="true">// TODO Auto-generated method stub </span>
        MyThread2 mt <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span>mt<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span>mt<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">MyThread2</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> ticket <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span>

        <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Runnable ticket = "</span> <span class="token operator">+</span> ticket<span class="token operator">--</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>ticket <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token keyword">break</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>原因是：MyThread创建了两个实例，⾃然会卖出两倍，属于⽤法错误 </p>
<h3 id="5、对守护线程的理解"><a href="#5、对守护线程的理解" class="headerlink" title="5、对守护线程的理解"></a>5、对守护线程的理解</h3><p><strong>守护线程：</strong>为所有⾮守护线程提供服务的线程；任何⼀个守护线程都是整个JVM中所有⾮守护线程的保姆；守护线程类似于整个进程的⼀个默默⽆闻的⼩喽喽；它的⽣死⽆关重要，它却依赖整个进程⽽运⾏；哪 </p>
<p>天其他线程结束了，没有要执⾏的了，程序就结束了，理都没理守护线程，就把它中断了； </p>
<p><strong>注意：</strong> 由于守护线程的终⽌是⾃身⽆法控制的，因此千万不要把IO、File等重要操作逻辑分配给它；因 </p>
<p>为它不靠谱； </p>
<p><strong>守护线程的作⽤是什么？</strong> </p>
<p>举例， GC垃圾回收线程：就是⼀个经典的守护线程，当我们的程序中不再有任何运⾏的Thread,程序就 </p>
<p>不会再产⽣垃圾，垃圾回收器也就⽆事可做，所以当垃圾回收线程是JVM上仅剩的线程时，垃圾回收线 </p>
<p>程会⾃动离开。它始终在低级别的状态中运⾏，⽤于实时监控和管理系统中的可回收资源。 </p>
<p><strong>应⽤场景：</strong></p>
<p>（1）来为其它线程提供服务⽀持的情况；</p>
<p>（2） 或者在任何情况下，程序结束时，这个线程 必须正常且⽴刻关闭，就可以作为守护线程来使⽤；反之，如果⼀个正在执⾏某个操作的线程必须要正 确地关闭掉否则就会出现不好的后果的话，那么这个线程就不能是守护线程，⽽是⽤户线程。通常都是 些关键的事务，⽐⽅说，数据库录⼊或者更新，这些操作都是不能中断的。 </p>
<ul>
<li><p>thread.setDaemon(true)必须在thread.start()之前设置，否则会跑出IllegalThreadStateException </p>
<p>异常。你不能把正在运⾏的常规线程设置为守护线程。 </p>
</li>
<li><p>在Daemon线程中产⽣的新线程也是Daemon的。 </p>
</li>
<li><p>守护线程不能⽤于去访问固有资源，⽐如读写操作或者计算逻辑。因为它会在任何时候甚⾄在⼀个操作 </p>
<p>的中间发⽣中断。 </p>
</li>
<li><p>Java⾃带的多线程框架，⽐如ExecutorService，会将守护线程转换为⽤户线程，所以如果要使⽤后台 </p>
<p>线程就不能⽤Java的线程池</p>
</li>
</ul>
<h3 id="6、ThreadLocal的底层原理"><a href="#6、ThreadLocal的底层原理" class="headerlink" title="6、ThreadLocal的底层原理"></a>6、ThreadLocal的底层原理</h3><ol>
<li><p>ThreadLocal是Java中所提供的线程本地存储机制，可以利⽤该机制将数据缓存在某个线程内部， 该线程可以在任意时刻、任意⽅法中获取缓存的数据 </p>
</li>
<li><p>ThreadLocal底层是通过ThreadLocalMap来实现的，每个Thread对象（注意不是ThreadLocal对 象）中都存在⼀个ThreadLocalMap，Map的key为ThreadLocal对象，Map的value为需要缓存的 值 </p>
</li>
<li><p>如果在线程池中使⽤ThreadLocal会造成内存泄漏，因为当ThreadLocal对象使⽤完之后，应该要 把设置的key，value，也就是Entry对象进⾏回收，但线程池中的线程不会回收，⽽线程对象是通过 强引⽤指向ThreadLocalMap，ThreadLocalMap也是通过强引⽤指向Entry对象，线程不被回收， Entry对象也就不会被回收，从⽽出现内存泄漏，解决办法是，在使⽤了ThreadLocal对象之后，⼿ 动调⽤ThreadLocal的remove⽅法，⼿动清楚Entry对象 </p>
</li>
<li><p>ThreadLocal经典的应⽤场景就是连接管理（⼀个线程持有⼀个连接，该连接对象可以在不同的⽅</p>
</li>
</ol>
<p>法之间进⾏传递，线程之间不共享同⼀个连接） </p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220729221815890.png" alt="image-20220729221815890"></p>
<p>由于每条线程均含有各自私有的 ThreadLocalMap容器，这些容器相互独立互不影响，因此不会存在线程安全性问题，从而也无需使用同步机制来保证多条线程访问容器的互斥性。</p>
<p><strong>使用场景：</strong></p>
<ul>
<li>在进行对象跨层传递的时候，使用ThreadLocal可以避免多次传递，打破层次间的约束</li>
<li>线程间数据隔离</li>
<li>进行事务操作，用于存储线程事务信息</li>
<li>数据库连接，Session会话管理</li>
</ul>
<blockquote>
<p>Spring框架在事务开始时会给当前线程绑定一个 Jdbc Connection，在整个事务过程都是使用该线程定的connection来执行数据库操作，实现了事务的隔离性， spring框架里面就是用的 Threadlocal来实现这种隔离。</p>
</blockquote>
<h3 id="6、ThreadLocal内存泄漏原因，如何避免？"><a href="#6、ThreadLocal内存泄漏原因，如何避免？" class="headerlink" title="6、ThreadLocal内存泄漏原因，如何避免？"></a>6、ThreadLocal内存泄漏原因，如何避免？</h3><p>如果在线程池中使用 ThreadLocal会造成内存泄漏，因为当 ThreadLocal对象使用完之后，应该要把设置的key，value也就是Entry对象进行回收，但线程池中的线程不会回收，而线程对象是通过强引用指向 ThreadLocalMap， ThreadLocalMap也是通过强引用指向Entry对象，线程不被回收，Entry对象也就不会被回收，从而出现内存泄漏。</p>
<p><strong>解决办法：</strong></p>
<ul>
<li>在使用了 ThreadLocal对象之后，手动调用ThreadLocal的 remove方法，手动清除Entry对象</li>
<li>将ThreadLocal变量定义成private static，这样就一直存在ThreadLocal的强引用，也就能保证任何时候都能将通过ThreadLocal的弱引用访问到Entry的value值，进而清除掉。</li>
</ul>
<h3 id="7、为什么⽤线程池？解释下线程池参数？"><a href="#7、为什么⽤线程池？解释下线程池参数？" class="headerlink" title="7、为什么⽤线程池？解释下线程池参数？"></a>7、为什么⽤线程池？解释下线程池参数？</h3><p>1、降低资源消耗；提⾼线程利⽤率，降低创建和销毁线程的消耗。 </p>
<p>2、提⾼响应速度；任务来了，直接有线程可⽤可执⾏，⽽不是先创建线程，再执⾏。 </p>
<p>3、提⾼线程的可管理性；线程是稀缺资源，使⽤线程池可以统⼀分配调优监控。 </p>
<ul>
<li><p>corePoolSize 代表核⼼线程数，也就是正常情况下创建⼯作的线程数，这些线程创建后并不会 消除，⽽是⼀种常驻线程 </p>
</li>
<li><p>maxinumPoolSize 代表的是最⼤线程数，它与核⼼线程数相对应，表示最⼤允许被创建的线程数，⽐如当前任务较多，将核⼼线程数都⽤完了，还⽆法满⾜需求时，此时就会创建新的线程，但 是线程池内线程总数不会超过最⼤线程数 </p>
</li>
<li><p>keepAliveTime 、 unit 表示超出核⼼线程数之外的线程的空闲存活时间，也就是核⼼线程不会消除，但是超出核⼼线程数的部分线程如果空闲⼀定的时间则会被消除,我们可以通过setKeepAliveTime 来设置空闲时间 </p>
</li>
<li><p>workQueue ⽤来存放待执⾏的任务，假设我们现在核⼼线程都已被使⽤，还有任务进来则全部放⼊队列，直到整个队列被放满但任务还再持续进⼊则会开始创建新的线程 </p>
</li>
<li><p>ThreadFactory 实际上是⼀个线程⼯⼚，⽤来⽣产线程执⾏任务。我们可以选择使⽤默认的创建⼯⼚，产⽣的线程都在同⼀个组内，拥有相同的优先级，且都不是守护线程。当然我们也可以选择⾃定义线程⼯⼚，⼀般我们会根据业务来制定不同的线程⼯⼚ </p>
</li>
<li><p>Handler 任务拒绝策略，有两种情况，第⼀种是当我们调⽤ shutdown 等⽅法关闭线程池后， 这时候即使线程池内部还有没执⾏完的任务正在执⾏，但是由于线程池已经关闭，我们再继续想线 程池提交任务就会遭到拒绝。另⼀种情况就是当达到最⼤线程数，线程池已经没有能⼒继续处理新 提交的任务时，这是也就拒绝</p>
</li>
</ul>
<h3 id="8、线程池的底层⼯作原理"><a href="#8、线程池的底层⼯作原理" class="headerlink" title="8、线程池的底层⼯作原理"></a>8、线程池的底层⼯作原理</h3><p>线程池内部是通过队列+线程实现的，当我们利⽤线程池执⾏任务时： </p>
<ol>
<li>如果此时线程池中的线程数量⼩于corePoolSize，即使线程池中的线程都处于空闲状态，也要创建</li>
</ol>
<p>新的线程来处理被添加的任务。 </p>
<ol start="2">
<li>如果此时线程池中的线程数量等于corePoolSize，但是缓冲队列workQueue未满，那么任务被放⼊</li>
</ol>
<p>缓冲队列。 </p>
<ol start="3">
<li>如果此时线程池中的线程数量⼤于等于corePoolSize，缓冲队列workQueue满，并且线程池中的数</li>
</ol>
<p>量⼩于maximumPoolSize，建新的线程来处理被添加的任务。 </p>
<ol start="4">
<li>如果此时线程池中的线程数量⼤于corePoolSize，缓冲队列workQueue满，并且线程池中的数量等</li>
</ol>
<p>于maximumPoolSize，那么通过 handler所指定的策略来处理此任务。 </p>
<ol start="5">
<li>当线程池中的线程数量⼤于 corePoolSize时，如果某线程空闲时间超过keepAliveTime，线程将被</li>
</ol>
<p>终⽌。这样，线程池可以动态的调整池中的线程数 </p>
<h3 id="9、线程池中阻塞队列的作⽤？为什么是先添加列队⽽不是先创建最⼤线程？"><a href="#9、线程池中阻塞队列的作⽤？为什么是先添加列队⽽不是先创建最⼤线程？" class="headerlink" title="9、线程池中阻塞队列的作⽤？为什么是先添加列队⽽不是先创建最⼤线程？"></a>9、线程池中阻塞队列的作⽤？为什么是先添加列队⽽不是先创建最⼤线程？</h3><ol>
<li><p>⼀般的队列只能保证作为⼀个有限⻓度的缓冲区，如果超出了缓冲⻓度，就⽆法保留当前的任务了， 阻塞队列通过阻塞可以保留住当前想要继续⼊队的任务。</p>
<p>阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程，使得线程进⼊wait状态，释放cpu资 </p>
<p>源。</p>
<p>阻塞队列⾃带阻塞和唤醒的功能，不需要额外处理，⽆任务执⾏时,线程池利⽤阻塞队列的take⽅法挂 </p>
<p>起，从⽽维持核⼼线程的存活、不⾄于⼀直占⽤cpu资源 </p>
</li>
<li><p>在创建新线程的时候，是要获取全局锁的，这个时候其它的就得阻塞，影响了整体效率。 </p>
<p>就好⽐⼀个企业⾥⾯有10个（core）正式⼯的名额，最多招10个正式⼯，要是任务超过正式⼯⼈数（</p>
<p>task &gt; core）的情况下，⼯⼚领导（线程池）不是⾸先扩招⼯⼈，还是这10⼈，但是任务可以稍微积 </p>
<p>压⼀下，即先放到队列去（代价低）。10个正式⼯慢慢⼲，迟早会⼲完的，要是任务还在继续增加，超 </p>
<p>过正式⼯的加班忍耐极限了（队列满了），就的招外包帮忙了（注意是临时⼯）要是正式⼯加上外包还 </p>
<p>是不能完成任务，那新来的任务就会被领导拒绝了（线程池的拒绝策略）。 </p>
<h3 id="10、线程池中线程复⽤原理"><a href="#10、线程池中线程复⽤原理" class="headerlink" title="10、线程池中线程复⽤原理"></a>10、线程池中线程复⽤原理</h3><p>线程池将线程和任务进⾏解耦，线程是线程，任务是任务，摆脱了之前通过 Thread 创建线程时的⼀个 </p>
<p>线程必须对应⼀个任务的限制。</p>
</li>
</ol>
<p>​	在线程池中，同⼀个线程可以从阻塞队列中不断获取新任务来执⾏，其核⼼原理在于线程池Thread </p>
<p>进⾏了封装，并不是每次执⾏任务都会调⽤ Thread.start() 来创建新线程，⽽是让每个线程去执⾏⼀ 个“循环任务”，在这个“循环任务”中不停检查是否有任务需要被执⾏，如果有则直接执⾏，也就是调⽤ </p>
<p>任务中的 run ⽅法，将 run ⽅法当成⼀个普通的⽅法执⾏，通过这种⽅式只使⽤固定的线程就将所有任 </p>
<p>务的 run ⽅法串联起来。 </p>
<h3 id="11、线程池中线程复⽤原理"><a href="#11、线程池中线程复⽤原理" class="headerlink" title="11、线程池中线程复⽤原理"></a>11、线程池中线程复⽤原理</h3><p>线程池将线程和任务进⾏解耦，线程是线程，任务是任务，摆脱了之前通过 Thread 创建线程时的⼀个 </p>
<p>线程必须对应⼀个任务的限制。 </p>
<p>在线程池中，同⼀个线程可以从阻塞队列中不断获取新任务来执⾏，其核⼼原理在于线程池对 Thread </p>
<p>进⾏了封装，并不是每次执⾏任务都会调⽤ Thread.start() 来创建新线程，⽽是让每个线程去执⾏⼀ </p>
<p>个“循环任务”，在这个“循环任务”中不停检查是否有任务需要被执⾏，如果有则直接执⾏，也就是调⽤ </p>
<p>任务中的 run ⽅法，将 run ⽅法当成⼀个普通的⽅法执⾏，通过这种⽅式只使⽤固定的线程就将所有任 </p>
<p>务的 run ⽅法串联起来。 </p>
<h3 id="12、什么是上下文切换"><a href="#12、什么是上下文切换" class="headerlink" title="12、什么是上下文切换?"></a>12、什么是上下文切换?</h3><p>巧妙地利用了时间片轮转的方式， CPU 给每个任务都服务一定的时间，然后把当前任务的状态保存下来，在加载下一任务的状态后，继续服务下一任务，任务的状态保存及再加载, 这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗 CPU 上执行变成了可能。</p>
<p>上下文：指某一时间点 CPU 寄存器和程序计数器的内容<br><strong>几种发生上下文切换的情况：</strong></p>
<ul>
<li>主动让出 CPU，比如调用了 sleep(), wait() 等。</li>
<li>时间片用完，因为操作系统要防止一个线程或者进程长时间占用CPU导致其他线程或者进程饿死。</li>
<li>调用了阻塞类型的系统中断，比如请求 IO，线程被阻塞，被终止或结束运行</li>
</ul>
<h3 id="13、线程之间如何进行通信"><a href="#13、线程之间如何进行通信" class="headerlink" title="13、线程之间如何进行通信"></a>13、线程之间如何进行通信</h3><ul>
<li>通过共享内存或基于网络通信</li>
<li>如果是基于共享内存进行通信，则需要考虑并发问题，什么时候阻塞，什么时候唤醒</li>
<li>想Java中的wait()、notify()就是阻塞唤醒</li>
<li>通过网络就比较简单，通过网络连接将数据发送给对方，当然也要考虑到并发问题，处理方式就是加锁等方式</li>
</ul>
<h3 id="14、阻塞队列的作用"><a href="#14、阻塞队列的作用" class="headerlink" title="14、阻塞队列的作用"></a>14、阻塞队列的作用</h3><p>—般的 队列只能保证作为一个有限长度的缓冲区，如果超出了缓冲长度，就无法保留当前的任务了，阻塞队列 通过阻塞可以保留住当前要继续入队的任务。</p>
<p>阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程，使得线程进入wait状态，释放CPU资源。<br>阻塞队列自带阻塞和唤醒的功能，不需要额外处理，无任务执行时线程池利用阻塞队列的take方法挂起，从而维持核心线程的存活、不至于一直占用cpu资源。</p>
<p><strong>为什么先添加队列而不是先创建最大线程？</strong><br>在创建新线程的时候，是要获取全局锁的，这个时候其它的就得阻塞，影晌了整体效率。 就好比一个企业甲面有10个(core)正式工的名额，最多招10个正式工，要是任务超过正式工人数(task&gt;core)的情况下，工厂领导(线程池)不是首先扩招工人，还是这10人，但是任务可以稍微积压一下，即先放到队列去(代价低)。10个正式工慢慢干，迟早会干完的，要是任务还在继续增加，超过正式工的加班忍耐极限了(队列满了)，就的招外包帮忙了(注意是临时工)要是正式工加上外包还是不能完成任务，那新来的任务就会被领导拒绝了(线程池的拒绝策略)</p>
<h3 id="15、Thread、Runable和Callable-三者区别？"><a href="#15、Thread、Runable和Callable-三者区别？" class="headerlink" title="15、Thread、Runable和Callable 三者区别？"></a>15、Thread、Runable和Callable 三者区别？</h3><ul>
<li>Thread 是一个抽象类，只能被继承，而 Runable、Callable 是接口，需要实现接口中的方法。继承 Thread 重写run()方法，实现Runable接口需要实现run()方法，而Callable是需要实现call()方法。</li>
<li>Thread 和 Runable 没有返回值，Callable 有返回值，返回值可以被 Future 拿到。</li>
<li>实现 Runable 接口的类不能直接调用start()方法，需要 new 一个 Thread 并发该实现类放入 Thread，再通过新建的 Thread 实例来调用start()方法。实现 Callable 接口的类需要借助 FutureTask (将该实现类放入其中)，再将 FutureTask 实例放入 Thread，再通过新建的 Thread 实例来调用start()方法。获取返回值只需要借助 FutureTask 实例调用get()方法即可！</li>
</ul>
<blockquote>
<p>什么是 FutureTask?<br>FutureTask 表示一个异步运算的任务。FutureTask 里面可以传入一个Callable 的具体实现类，可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。只有当运算完成的时候结果才能取回，如果运算尚未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了 Callable 和 Runnable 的对象进行包装，由于 FutureTask 也是Runnable 接口的实现类，所以 FutureTask 也可以放入线程池中。</p>
</blockquote>
<h3 id="16、线程的-run-和-start-有什么区别？"><a href="#16、线程的-run-和-start-有什么区别？" class="headerlink" title="16、线程的 run()和 start()有什么区别？"></a>16、线程的 run()和 start()有什么区别？</h3><p>通过调用 Thread 类的 start()方法来启动一个线程， 这时此线程是处于就绪状态， 并没有运行。这时无需等待 run 方法体代码执行完毕，可以直接继续执行下面的代码。</p>
<p>方法 run()称为线程体，它包含了要执行的这个线程的内容，线程就进入了运行状态，开始运行 run 函数当中的代码。 run 方法运行结束， 此线程终止。然后 CPU 再调度其它线程。</p>
<blockquote>
<p>run() 可以重复调用，而 start()只能调用一次。</p>
</blockquote>
<p><strong>为什么我们调用 start() 方法时会执行 run() 方法，为什么我们不能直接调用run() 方法？</strong></p>
<p>如果直接执行 run() 方法，会把 run 方法当成一个 main 线程下的普通方法去执行，并不会在某个线程中执行它，所以这并不是多线程工作。调用 start 方法方可启动线程并使线程进入就绪状态，而 run 方法只是 thread 的一个普通方法调用，还是在主线程里执行。</p>
<h2 id="二、并发相关"><a href="#二、并发相关" class="headerlink" title="二、并发相关"></a>二、并发相关</h2><h3 id="1、并发、并⾏、串⾏之间的区别"><a href="#1、并发、并⾏、串⾏之间的区别" class="headerlink" title="1、并发、并⾏、串⾏之间的区别"></a>1、并发、并⾏、串⾏之间的区别</h3><ol>
<li><p>串⾏在时间上不可能发⽣重叠，前⼀个任务没搞定，下⼀个任务就只能等着 </p>
</li>
<li><p>并⾏在时间上是重叠的，两个任务在同⼀时刻互不⼲扰的同时执⾏。 </p>
</li>
<li><p>并发允许两个任务彼此⼲扰。统⼀时间点、只有⼀个任务运⾏，交替执⾏</p>
</li>
</ol>
<h3 id="2、并发的三⼤特性"><a href="#2、并发的三⼤特性" class="headerlink" title="2、并发的三⼤特性"></a>2、并发的三⼤特性</h3><p><strong>原⼦性</strong> </p>
<p>原⼦性是指在⼀个操作中cpu不可以在中途暂停然后再调度，即不被中断操作，要不全部执⾏完成，要 </p>
<p>不都不执⾏。就好⽐转账，从账户A向账户B转1000元，那么必然包括2个操作：从账户A减去1000元， </p>
<p>往账户B加上1000元。2个操作必须全部完成。</p>
<pre class=" language-java"><code class="language-java">
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">long</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        count<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>1：将 count 从主存读到⼯作内存中的副本中 </p>
<p>2：+1的运算 </p>
<p>3：将结果写⼊⼯作内存 </p>
<p>4：将⼯作内存的值刷回主存(什么时候刷⼊由操作系统决定，不确定的) </p>
<blockquote>
<p>那程序中原⼦性指的是最⼩的操作单元，⽐如⾃增操作，它本身其实并不是原⼦性操作，分了3步的， </p>
<p>包括读取变量的原始值、进⾏加1操作、写⼊⼯作内存。所以在多线程中，有可能⼀个线程还没⾃增完， </p>
<p>可能才执⾏到第⼆部，另⼀个线程就已经读取了值，导致结果错误。那如果我们能保证⾃增操作是⼀个 </p>
<p>原⼦性的操作，那么就能保证其他线程读取到的⼀定是⾃增后的数据。 </p>
</blockquote>
<p><strong>关键字：</strong>synchronized </p>
<p><strong>可⻅性</strong> </p>
<p>当多个线程访问同⼀个变量时，⼀个线程修改了这个变量的值，其他线程能够⽴即看得到修改的值。 </p>
<p>若两个线程在不同的cpu，那么线程1改变了i的值还没刷新到主存，线程2⼜使⽤了i，那么这个i值肯定还 </p>
<p>是之前的，线程1对变量的修改线程没看到这就是可⻅性问题。</p>
<pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">//线程1</span>
    <span class="token keyword">boolean</span> stop <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token operator">!</span>stop<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">//线程2</span>
    stop <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
</code></pre>
<p>如果线程2改变了stop的值，线程1⼀定会停⽌吗？不⼀定。当线程2更改了stop变量的值之后，但是还 </p>
<p>没来得及写⼊主存当中，线程2转去做其他事情了，那么线程1由于不知道线程2对stop变量的更改，因 </p>
<p>此还会⼀直循环下去。 </p>
<p><strong>关键字：</strong>volatile、synchronized、final</p>
<p><strong>有序性</strong> </p>
<p>虚拟机在进⾏代码编译时，对于那些改变顺序之后不会对最终结果造成影响的代码，虚拟机不⼀定会按 </p>
<p>照我们写的代码的顺序来执⾏，有可能将他们重排序。实际上，对于有些代码进⾏重排序之后，虽然对 </p>
<p>变量的值没有造成影响，但有可能会出现线程安全问题。 </p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test</span> <span class="token punctuation">{</span>
    <span class="token keyword">int</span> a <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    bool flag <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        a <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//1</span>
        flag <span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">;</span><span class="token comment" spellcheck="true">//2</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">multiply</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>flag<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment" spellcheck="true">//3</span>
            <span class="token keyword">int</span> ret <span class="token operator">=</span> a <span class="token operator">*</span> a<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//4</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>write⽅法⾥的1和2做了重排序，线程1先对flag赋值为true，随后执⾏到线程2，ret直接计算出结果，再 </p>
<p>到线程1，这时候a才赋值为2,很明显迟了⼀步</p>
<p><strong>关键字：</strong>volatile、synchronized </p>
<ul>
<li><p>volatile本身就包含了禁⽌指令重排序的语义，⽽synchronized关键字是由“⼀个变量在同⼀时刻只允许⼀条线程对其进⾏lock操作”这条规则明确的。 </p>
</li>
<li><p>synchronized关键字同时满⾜以上三种特性，但是volatile关键字不满⾜原⼦性。 </p>
</li>
<li><p>在某些情况下，volatile的同步机制的性能确实要优于锁(使⽤synchronized关键字或 java.util.concurrent包⾥⾯的锁)，因为volatile的总开销要⽐锁低。 </p>
</li>
<li><p>我们判断使⽤volatile还是加锁的唯⼀依据就是volatile的语义能否满⾜使⽤的场景(原⼦性)</p>
</li>
</ul>
<h3 id="3、如何理解volatile关键字"><a href="#3、如何理解volatile关键字" class="headerlink" title="3、如何理解volatile关键字"></a>3、如何理解volatile关键字</h3><p>保证被volatile修饰的共享变量对所有线程总是可⻅的，也就是当⼀个线程修改了⼀个被volatile修饰共 </p>
<p>享变量的值，新值总是可以被其他线程⽴即得知。 </p>
<p>如果线程2改变了stop的值，线程1⼀定会停⽌吗？不⼀定。当线程2更改了stop变量的值之后，但是还 </p>
<p>没来得及写⼊主存当中，线程2转去做其他事情了，那么线程1由于不知道线程2对stop变量的更改，因 </p>
<p>此还会⼀直循环下去。 </p>
<pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">//线程1 </span>
<span class="token keyword">boolean</span> stop <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> 
<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token operator">!</span>stop<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token comment" spellcheck="true">//线程2</span>
stop <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
</code></pre>
<p>禁⽌指令重排序优化</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">int</span> a <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> 
bool flag <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> 

<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 
    a <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//1 </span>
    flag <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//2 </span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">multiply</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 
    <span class="token keyword">if</span> <span class="token punctuation">(</span>flag<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//3 </span>
        <span class="token keyword">int</span> ret <span class="token operator">=</span> a <span class="token operator">*</span> a<span class="token punctuation">;</span><span class="token comment" spellcheck="true">//4 </span>
    <span class="token punctuation">}</span> 
<span class="token punctuation">}</span>
</code></pre>
<p>write⽅法⾥的1和2做了重排序，线程1先对flag赋值为true，随后执⾏到线程2，ret直接计算出结果，再 </p>
<p>到线程1，这时候a才赋值为2,很明显迟了⼀步。但是⽤volatile修饰之后就变得不⼀样了：</p>
<ol>
<li><p>使⽤volatile关键字会强制将修改的值⽴即写⼊主存； </p>
</li>
<li><p>使⽤volatile关键字的话，当线程2进⾏修改时，会导致线程1的⼯作内存中缓存变量stop的缓存⾏⽆ 效（反映到硬件层的话，就是CPU的L1或者L2缓存中对应的缓存⾏⽆效）； </p>
</li>
<li><p>由于线程1的⼯作内存中缓存变量stop的缓存⾏⽆效，所以线程1再次读取变量stop的值时会去主存</p>
</li>
</ol>
<p>读取。</p>
<p>inc++; 其实是两个步骤，先加加，然后再赋值。不是原⼦性操作，所以volatile不能保证线程安全。 </p>
<h3 id="4、ReentrantLock中的公平锁和⾮公平锁的底层实现"><a href="#4、ReentrantLock中的公平锁和⾮公平锁的底层实现" class="headerlink" title="4、ReentrantLock中的公平锁和⾮公平锁的底层实现"></a>4、ReentrantLock中的公平锁和⾮公平锁的底层实现</h3><p>⾸先不管是公平锁和⾮公平锁，它们的底层实现都会使⽤AQS来进⾏排队，它们的区别在于：线程在使 </p>
<p>⽤lock()⽅法加锁时，如果是公平锁，会先检查AQS队列中是否存在线程在排队，如果有线程在排队， </p>
<p>则当前线程也进⾏排队，如果是⾮公平锁，则不会去检查是否有线程在排队，⽽是直接竞争锁。 </p>
<p>不管是公平锁还是⾮公平锁，⼀旦没竞争到锁，都会进⾏排队，当锁释放时，都是唤醒排在最前⾯的线 </p>
<p>程，所以⾮公平锁只是体现在了线程加锁阶段，⽽没有体现在线程被唤醒阶段。 另外，ReentrantLock是可重⼊锁，不管是公平锁还是⾮公平锁都是可重⼊的。 </p>
<h3 id="5、ReentrantLock中tryLock-和lock-⽅法的区别"><a href="#5、ReentrantLock中tryLock-和lock-⽅法的区别" class="headerlink" title="5、ReentrantLock中tryLock()和lock()⽅法的区别"></a>5、ReentrantLock中tryLock()和lock()⽅法的区别</h3><ol>
<li>tryLock()表示尝试加锁，可能加到，也可能加不到，该⽅法不会阻塞线程，如果加到锁则返回</li>
</ol>
<p>true，没有加到则返回false </p>
<ol start="2">
<li>lock()表示阻塞加锁，线程会阻塞直到加到锁，⽅法也没有返回值</li>
</ol>
<h3 id="6、CountDownLatch和Semaphore的区别和底层原理"><a href="#6、CountDownLatch和Semaphore的区别和底层原理" class="headerlink" title="6、CountDownLatch和Semaphore的区别和底层原理"></a>6、CountDownLatch和Semaphore的区别和底层原理</h3><p>CountDownLatch表示计数器，可以给CountDownLatch设置⼀个数字，⼀个线程调⽤ </p>
<p>CountDownLatch的await()将会阻塞，其他线程可以调⽤CountDownLatch的countDown()⽅法来对 </p>
<p>CountDownLatch中的数字减⼀，当数字被减成0后，所有await的线程都将被唤醒。 </p>
<p>对应的底层原理就是，调⽤await()⽅法的线程会利⽤AQS排队，⼀旦数字被减为0，则会将AQS中 </p>
<p>排队的线程依次唤醒。</p>
<p>Semaphore表示信号量，可以设置许可的个数，表示同时允许最多多少个线程使⽤该信号量，通 </p>
<p>过acquire()来获取许可，如果没有许可可⽤则线程阻塞，并通过AQS来排队，可以通过release() </p>
<p>⽅法来释放许可，当某个线程释放了某个许可后，会从AQS中正在排队的第⼀个线程开始依次唤 </p>
<p>醒，直到没有空闲许可。 </p>
<h3 id="7、Sychronized的偏向锁、轻量级锁、重量级锁"><a href="#7、Sychronized的偏向锁、轻量级锁、重量级锁" class="headerlink" title="7、Sychronized的偏向锁、轻量级锁、重量级锁"></a>7、Sychronized的偏向锁、轻量级锁、重量级锁</h3><ol>
<li><p>偏向锁：在锁对象的对象头中记录⼀下当前获取到该锁的线程ID，该线程下次如果⼜来获取该锁就可以直接获取到了 </p>
</li>
<li><p>轻量级锁：由偏向锁升级⽽来，当⼀个线程获取到锁后，此时这把锁是偏向锁，此时如果有第⼆个 线程来竞争锁，偏向锁就会升级为轻量级锁，之所以叫轻量级锁，是为了和重量级锁区分开来，轻量级锁底层是通过⾃旋来实现的，并不会阻塞线程 </p>
</li>
<li><p>如果⾃旋次数过多仍然没有获取到锁，则会升级为重量级锁，重量级锁会导致线程阻塞 </p>
</li>
<li><p>⾃旋锁：⾃旋锁就是线程在获取锁的过程中，不会去阻塞线程，也就⽆所谓唤醒线程，阻塞和唤醒 这两个步骤都是需要操作系统去进⾏的，⽐较消耗时间，⾃旋锁是线程通过CAS获取预期的⼀个标 记，如果没有获取到，则继续循环获取，如果获取到了则表示获取到了锁，这个过程线程⼀直在运⾏中，相对⽽⾔没有使⽤太多的操作系统资源，⽐较轻量。</p>
</li>
</ol>
<h3 id="8、Sychronized和ReentrantLock的区别"><a href="#8、Sychronized和ReentrantLock的区别" class="headerlink" title="8、Sychronized和ReentrantLock的区别"></a>8、Sychronized和ReentrantLock的区别</h3><ol>
<li><p>sychronized是⼀个关键字，ReentrantLock是⼀个类 </p>
</li>
<li><p>sychronized会⾃动的加锁与释放锁，ReentrantLock需要程序员⼿动加锁与释放锁 </p>
</li>
<li><p>sychronized的底层是JVM层⾯的锁，ReentrantLock是API层⾯的锁 </p>
</li>
<li><p>sychronized是⾮公平锁，ReentrantLock可以选择公平锁或⾮公平锁 </p>
</li>
<li><p>sychronized锁的是对象，锁信息保存在对象头中，ReentrantLock通过代码中int类型的state标识 来标识锁的状态 </p>
</li>
<li><p>sychronized底层有⼀个锁升级的过程</p>
</li>
</ol>
<blockquote>
<p>Lock接口<br>在jdk1.5以后，增加了juc并发包且提供了Lock接口用来实现锁的功能，它除了提供了与synchroinzed关键字类似的同步功能，还提供了比synchronized更灵活api实现。可以把 Lock 看成是 synchronized 的扩展版，Lock 提供了无条件的、可轮询的(tryLock 方法)、定时的(tryLock 带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition 方法)锁操作。另外 Lock 的实现类基本都支持非公平锁(默认)和公平锁，synchronized 只支持非公平锁。</p>
</blockquote>
<blockquote>
<p><strong>ReentrantLock</strong><br><strong>ReentantLock 继承接口 Lock 并实现了接口中定义的方法，他是一种可重入锁，除了能完成 synchronized 所能完成的所有工作外，还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。</strong></p>
</blockquote>
<blockquote>
<p><strong>Sychronized和Lock的区别</strong></p>
<ul>
<li>synchronized 可以给类. 方法. 代码块加锁；而 lock 只能给代码块加锁。</li>
<li>synchronized 不需要手动获取锁和释放锁，使用简单，发生异常会自动释放锁，不会造成死锁；而 lock 需要自己加锁和释放锁，如果使用不当没有 unLock()去释放锁就会造成死锁。</li>
<li>通过 Lock 可以知道有没有成功获取锁，而 synchronized 却无法办到</li>
</ul>
</blockquote>
<blockquote>
<p><strong>Sychronized和volatile的区别</strong></p>
<p><code>volatile</code> 解决的是内存可见性问题，会使得所有对 <code>volatile</code> 变量的读写都直接写入主存，即 <strong>保证了变量的可见性</strong>。</p>
<p><code>synchronized</code> 解决的事执行控制的问题，它会阻止其他线程获取当前对象的监控锁，这样一来就让当前对象中被 <code>synchronized</code> 关键字保护的代码块无法被其他线程访问，也就是无法并发执行。而且，<code>synchronized</code> 还会创建一个 <strong>内存屏障</strong>，内存屏障指令保证了所有 CPU 操作结果都会直接刷到主存中，从而 <strong>保证操作的内存可见性</strong>，同时也使得这个锁的线程的所有操作都 <code>happens-before</code> 于随后获得这个锁的线程的操作。</p>
<p>两者的区别主要有如下：</p>
<ol>
<li>volatile 本质是在告诉 JVM 当前变量在寄存器（工作内存）中的值是不确定的，需要从主存中读取； synchronized 则是锁定当前变量，只有当前线程可以访问该变量，其他线程被阻塞住。</li>
<li>volatile <strong>仅能使用在变量级别</strong>；synchronized 则可以使用在 <strong>变量. 方法. 和类级别的</strong></li>
<li>volatile 仅能实现变量的修改可见性，<strong>不能保证原子性</strong>；而synchronized 则可以 <strong>保证变量的修改可见性和原子性</strong></li>
<li>volatile <strong>不会造成线程的阻塞</strong>；synchronized <strong>可能会造成线程的阻塞</strong>。</li>
<li>volatile 标记的变量不会被编译器优化；synchronized 标记的变量可以被编译器优化。</li>
</ol>
</blockquote>
<h3 id="9、谈谈你对AQS的理解，AQS如何实现可重⼊锁？"><a href="#9、谈谈你对AQS的理解，AQS如何实现可重⼊锁？" class="headerlink" title="9、谈谈你对AQS的理解，AQS如何实现可重⼊锁？"></a>9、谈谈你对AQS的理解，AQS如何实现可重⼊锁？</h3><ol>
<li><p>AQS是⼀个JAVA线程同步的框架。是JDK中很多锁⼯具的核⼼实现框架。 </p>
</li>
<li><p>在AQS中，维护了⼀个信号量state和⼀个线程组成的双向链表队列。其中，这个线程队列，就是⽤ 来给线程排队的，⽽state就像是⼀个红绿灯，⽤来控制线程排队或者放⾏的。 在不同的场景下， 有不⽤的意义。</p>
</li>
</ol>
<p><img src="C:\Users\15433\AppData\Roaming\Typora\typora-user-images\image-20220906151951562.png" alt="image-20220906151951562"></p>
<ol start="3">
<li>在可重⼊锁这个场景下，state就⽤来表示加锁的次数。0标识⽆锁，每加⼀次锁，state就加1。释 放锁state就减1。</li>
</ol>
<h3 id="10、synchronized关键字的作用"><a href="#10、synchronized关键字的作用" class="headerlink" title="10、synchronized关键字的作用"></a>10、synchronized关键字的作用</h3><p>synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁，同时属于可重入锁。</p>
<p>synchronized 关键字解决的是多个线程之间访问资源的同步性，synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。</p>
<h4 id="三种使用方式："><a href="#三种使用方式：" class="headerlink" title="三种使用方式："></a><strong>三种使用方式：</strong></h4><ul>
<li><p>修饰实例方法：作用于当前对象实例加锁，进入同步代码前要获得当前对象实例的锁</p>
</li>
<li><p>修饰静态方法：也就是给当前类加锁，会作用于类的所有对象实例，因为静态成员不属于任何一个实例对象，是类成员。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法，而线程B需要调用这个实例对象所属类的静态 synchronized 方法，是允许的，不会发生互斥现象，因为访问静态synchronized 方法占用的锁是当前类的锁，而访问非静态 synchronized 方法占用的锁是当前实例对象锁。</p>
</li>
<li><p>修饰代码块: 指定加锁对象，对给定对象加锁，进入同步代码库前要获得给定对象的锁</p>
</li>
</ul>
<blockquote>
<p>总结： synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中，字符串常量池具有缓存功能</p>
</blockquote>
<h3 id="11、volatile-关键字的作用"><a href="#11、volatile-关键字的作用" class="headerlink" title="11、volatile 关键字的作用"></a>11、volatile 关键字的作用</h3><p>Java 提供了 volatile 关键字是线程同步的轻量级实现，用来保证可见性和有序性（禁止指令重排），volatile 常用于多线程环境下的单次操作(单次读或者单次写)。</p>
<ul>
<li><p>对于加了 volatile关键字的成员变量，在对这个变量进行修改时，全直接将CPU高级缓存中的数据送回到主内存,对这个变量的读取也会直接从主内存中读取，从而保证了可见性</p>
</li>
<li><p>在对 volatile修饰的成员变量进行读写时，会插入内存屏障，而内存屏障可以达到禁止重排序的效果，从而可以保证有序性</p>
</li>
</ul>
<p><strong>volatile可以和CAS 结合，来保证原子性。</strong></p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906151342701.png" alt="image-20220906151342701"></p>
<h3 id="12、volatile是怎样实现的？"><a href="#12、volatile是怎样实现的？" class="headerlink" title="12、volatile是怎样实现的？"></a>12、volatile是怎样实现的？</h3><h4 id="volatile实现内存可见性原理："><a href="#volatile实现内存可见性原理：" class="headerlink" title="volatile实现内存可见性原理："></a>volatile实现内存可见性原理：</h4><p>一个问题：本地内存和主内存之间的值不一致，导致内存不可见。<br>在生成汇编代码时会在volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令。将当前处理器缓存行的数据写回系统内存，这个写回内存的操作会使得其他CPU里缓存了该内存地址的数据无效，当处理器发现本地缓存失效后，就会从内存中重读该变量数据，即可以获取当前最新值。这样针对volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值。</p>
<h4 id="volatile实现有序性原理："><a href="#volatile实现有序性原理：" class="headerlink" title="volatile实现有序性原理："></a><strong>volatile实现有序性原理：</strong></h4><p>为了实现volatile的内存语义，编译器在生成字节码时会通过插入内存屏障来禁止指令重排序。<br>内存屏障：内存屏障是一种CPU指令，它的作用是对该指令前和指令后的一些操作产生一定的约束，保证一些操作按顺序执行。</p>
<h3 id="13、什么是CAS"><a href="#13、什么是CAS" class="headerlink" title="13、什么是CAS?"></a>13、什么是CAS?</h3><p>CAS即Compare And Swap，<strong>比较并替换</strong>。是一条CPU并发原语，Java中可以通过CAS操作来保证<a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=%E5%8E%9F%E5%AD%90%E6%80%A7&amp;spm=1001.2101.3001.7020">原子性</a>，它的功能是判断内存某个位置的值是否为预期值，如果是则更新为新的值，否则什么都不做或重来，<strong>当它重来重试的这种行为称为——自旋</strong>这个过程是原子的。</p>
<p>CAS并发原语提现在Java语言中就是sun.misc.UnSafe类中的各个方法。调用UnSafe类中的CAS方法，JVM会帮我实现CAS汇编指令。这是一种完全依赖于硬件功能，通过它实现了原子操作。原语的执行必须是连续的，在执行过程中不允许中断，也即是说CAS是一条原子指令，不会造成所谓的数据不一致的问题。</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906200954601.png" alt="image-20220906200954601"></p>
<blockquote>
<p>Unsafe：CAS的核心类，Java方法无法直接访问内存，需要通过本地方法native来访问，在Unsafe中所有方法都是native方法，用来直接操作内存，执行相应的任务。</p>
</blockquote>
<blockquote>
<p>CAS有3个操作数，内存值V，旧的预期值A，要修改的新值B。当且仅当预期值A和内存值V相同时，将内存值V修改为B，否则什么都不做。类似于乐观锁。CAS自旋的概率会比较大，从而浪费更多的CPU资源。在这个过程中可能存在ABA问题：<br>当你获得对象当前数据后，在准备修改为新值前，对象的值被其他线程连续修改了两次（A-&gt;B-&gt;A），而经过两次修改后，对象的值又恢复为旧值，这样当前线程无法正确判断这个对象是否修改过。</p>
</blockquote>
<h4 id="CAS缺陷"><a href="#CAS缺陷" class="headerlink" title="CAS缺陷"></a>CAS缺陷</h4><p><strong>1. ABA 问题</strong></p>
<p>并发环境下，假设初始条件是A，去修改数据时，发现是A就会执行修改。但是看到的虽然是A，中间可能发生了A变B，B又变回A的情况。此时A已经非彼A，数据即使成功修改，也可能有问题。</p>
<p>可以通过AtomicStampedReference<strong>解决ABA问题</strong>，它，一个带有标记的原子引用类，通过控制变量值的版本来保证CAS的正确性。</p>
<p><strong>2. 循环时间长开销</strong></p>
<p>自旋CAS，如果一直循环执行，一直不成功，会给CPU带来非常大的执行开销。</p>
<p>很多时候，CAS思想体现，是有个自旋次数的，就是为了避开这个耗时问题~</p>
<p><strong>3. 只能保证一个变量的原子操作。</strong></p>
<p>CAS 保证的是对一个变量执行操作的原子性，如果对多个变量操作时，CAS 目前无法直接保证操作的原子性的。</p>
<p><strong>可以通过这两个方式解决这个问题</strong>：</p>
<ul>
<li>使用互斥锁来保证原子性；</li>
<li>将多个变量封装成对象，通过AtomicReference来保证原子性。</li>
</ul>
<h4 id="ABA的问题的解决方式："><a href="#ABA的问题的解决方式：" class="headerlink" title="ABA的问题的解决方式："></a>ABA的问题的解决方式：</h4><ul>
<li>ABA的解决方法也很简单，就是利用版本号。给变量加上一个版本号，每次变量更新的时候就把版本号加1，这样即使E的值从A—&gt;B—&gt;A，版本号也发生了变化，这样就解决了CAS出现的ABA问题。基于CAS的乐观锁也是这个实现原理。</li>
<li>JDK1.5时可以利用AtomicStampedReference类来解决这个问题，AtomicStampedReference内部不仅维护了对象值，还维护了一个时间戳。当AtomicStampedReference对应的数值被修改时，除了更新数据本身外，还必须要更新时间戳，对象值和时间戳都必须满足期望值，写入才会成功</li>
</ul>
<p>自旋：当多个线程同时操作一个共享变量时，只有一个线程可以对变量进行成功更新，其他线程均会失败，但是失败并不会被挂起，进行再次尝试，也就是自旋。Java中的自旋锁就是利用CAS来实现的。</p>
<h3 id="14、synchronized-关键字和-volatile-关键字的区别"><a href="#14、synchronized-关键字和-volatile-关键字的区别" class="headerlink" title="14、synchronized 关键字和 volatile 关键字的区别"></a>14、synchronized 关键字和 volatile 关键字的区别</h3><p>synchronized 关键字和 volatile 关键字是两个互补的存在，而不是对立的存在！</p>
<ul>
<li><p>volatile 关键字是线程同步的轻量级实现，所以 volatile 性能肯定比synchronized关键字要好 。</p>
</li>
<li><p>volatile 关键字只能用于变量，而synchronized 关键字可以修饰方法以及代码块 。</p>
</li>
<li><p>volatile 关键字能保证数据的可见性，但不能保证数据的原子性。synchronized 关键字两者都能保证。</p>
</li>
<li><p>volatile关键字主要用于解决变量在多个线程之间的可见性，而 synchronized 关键字解决的是多个线程之间访问资源的同步性。</p>
</li>
</ul>
<h3 id="15、锁分类"><a href="#15、锁分类" class="headerlink" title="15、锁分类"></a>15、锁分类</h3><h4 id="是否锁同步资源：乐观锁、悲观锁"><a href="#是否锁同步资源：乐观锁、悲观锁" class="headerlink" title="是否锁同步资源：乐观锁、悲观锁"></a>是否锁同步资源：乐观锁、悲观锁</h4><p>乐观锁：一种乐观思想，即认为读多写少，遇到并发写的可能性低，每次去拿数据的时候都认为别人不会修改，所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据，采取在写时先读出当前版本号，然后加锁操作（比较跟上一次的版本号，如果一样则更新），如果失败则要重复读-比较-写的操作。</p>
<p>判断规则：</p>
<ul>
<li>版本号机制Version</li>
<li>CAS</li>
</ul>
<blockquote>
<p>java 中的乐观锁基本都是通过 CAS 操作实现的，CAS 是一种更新的原子操作，比较当前值跟传入值是否一样，一样则更新，否则失败。</p>
</blockquote>
<p>悲观锁：一种悲观思想，即认为写多，遇到并发写的可能性高，每次去拿数据的时候都认为别人会修改，所以每次在读写数据的时候都会上锁，这样别人想读写这个数据就会 block 直到拿到锁。java中的悲观锁就是synchronized、lock；</p>
<h4 id="线程是否阻塞：自旋锁"><a href="#线程是否阻塞：自旋锁" class="headerlink" title="线程是否阻塞：自旋锁"></a>线程是否阻塞：自旋锁</h4><p>如果持有锁的线程能在很短时间内释放锁资源，那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态，它们只需要等一等（自旋），等持有锁的线程释放锁后即可立即获取锁，这样就避免用户线程和内核的切换的消耗。</p>
<blockquote>
<p>线程自旋是需要消耗 CPU 的，说白了就是让 CPU在做无用功，如果一直获取不到锁，那线程也不能一直占用CPU自旋做无用功，所以需要设定一个自旋等待的最大时间。如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁，就会导致其它争用锁的线程在最大等待时间内还是获取不到锁，这时争用线程会停止自旋进入阻塞状态。</p>
</blockquote>
<p><strong>自旋锁的优点：</strong><br>自旋锁尽可能的减少线程的阻塞，这对于锁的竞争不激烈，且占用锁时间非常短的代码块来说性能能大幅度的提升，因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗，这些操作会导致线程发生两次上下文切换！</p>
<h4 id="线程竞争时是否排队：公平锁、非公平锁"><a href="#线程竞争时是否排队：公平锁、非公平锁" class="headerlink" title="线程竞争时是否排队：公平锁、非公平锁"></a>线程竞争时是否排队：公平锁、非公平锁</h4><ul>
<li><p>公平锁：锁的分配机制是公平的，加锁前检查是否有排队等待的线程，优先排队等待的线程，先来先得</p>
</li>
<li><p>非公平锁：加锁时不考虑排队等待问题，JVM 按随机、就近原则分配锁的机制则称为不公平锁，非公平锁实际执行的效率要远远超出公平锁（5-10倍），除非程序有特殊需要，否则最常用非公平锁的分配机制。</p>
</li>
</ul>
<p><strong>为什么默认的是非公平锁，而不是公平锁？</strong></p>
<ul>
<li>恢复挂起的线程到真正锁的获取是有时间差的，从开发人员来看这个时间微乎其微，但是从cpu角度来看，这个时间差的存在还是非常明显的。所以非公平锁能更充分的利用cpu的时间片，尽量减少cpu空闲状态时间</li>
<li>使用多线程更重要的考量点是线程切换的开销，当采用非公平锁时，当一个线程请求锁获取同步状态，然后释放同步状态，所以刚释放锁的线程在此刻再次获取同步状态的几率就变得非常大，所以就减少了线程的开销。</li>
</ul>
<h4 id="线程中的多个流程是否可以获取同一把锁：可重入锁（递归锁）"><a href="#线程中的多个流程是否可以获取同一把锁：可重入锁（递归锁）" class="headerlink" title="线程中的多个流程是否可以获取同一把锁：可重入锁（递归锁）"></a>线程中的多个流程是否可以获取同一把锁：可重入锁（递归锁）</h4><p>可重入锁，也叫做递归锁，指的是同一线程外层函数获得锁之后 ，内层递归函数仍然有获取该锁的代码，但不受影响。在 JAVA 环境下 ReentrantLock 和 synchronized 都是可重入锁。</p>
<h4 id="多线程之间能不能共享同一把锁：共享锁和排他锁"><a href="#多线程之间能不能共享同一把锁：共享锁和排他锁" class="headerlink" title="多线程之间能不能共享同一把锁：共享锁和排他锁"></a>多线程之间能不能共享同一把锁：共享锁和排他锁</h4><ul>
<li><p>共享锁：共享锁则允许多个线程同时获取锁，并发访问 共享资源，如：ReadWriteLock。共享锁则是一种乐观锁，它放宽了加锁策略，允许多个执行读操作的线程同时访问共享资源。</p>
</li>
<li><p>排他锁：每次只能有一个线程能持有锁，ReentrantLock 就是以独占方式实现的互斥锁。独占锁是一种悲观保守的加锁策略，它避免了读/读冲突，如果某个只读线程获取锁，则其他读线程都只能等待，这种情况下就限制了不必要的并发性，因为读操作并不会影响数据的一致性。</p>
</li>
</ul>
<h4 id="锁状态：无锁、偏向锁、轻量级锁、重量级锁"><a href="#锁状态：无锁、偏向锁、轻量级锁、重量级锁" class="headerlink" title="锁状态：无锁、偏向锁、轻量级锁、重量级锁"></a>锁状态：无锁、偏向锁、轻量级锁、重量级锁</h4><p>锁的状态总共有四种：无锁状态、偏向锁、轻量级锁和重量级锁。</p>
<p>锁升级：随着锁的竞争，锁可以从偏向锁升级到轻量级锁，再升级的重量级锁（但是锁的升级是单向的，<br>也就是说只能从低到高升级，不会出现锁的降级）。</p>
<ul>
<li><p>无锁没有对资源进行锁定，所有的线程都能访问并修改同一个资源，但同时只有一个线程能修改成功。</p>
</li>
<li><p>偏向锁是指一段同步代码一直被一个线程所访问，那么该线程会自动获取锁，降低获取锁的代价。在大多数情况下，锁总是由同一线程多次获得，不存在多线程竞争，所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能</p>
</li>
<li><p>轻量级锁：当锁是偏向锁的时候，被另外的线程所访问，偏向锁就会升级为轻量级锁，其他线程会通过自旋的形式尝试获取锁，不会阻塞，从而提高性能。</p>
</li>
<li><p>重量级锁：升级为重量级锁时，锁标志的状态值变为“10”，此时Mark Word中存储的是指向重量级锁的指针，此时等待锁的线程都会进入阻塞状态。<br><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906152615925.png" alt="image-20220906152615925"></p>
</li>
</ul>
<h4 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h4><p>两个或两个以上的线程在执行过程中，由于竞争资源或者由于彼此通信而造成的一种阻塞的现象，若无外力作用，它们都将无法让程序进行下去！</p>
<p><strong>死锁必须具备以下四个条件</strong></p>
<ul>
<li>互斥条件：该资源任意一个时刻只由一个线程占用。</li>
<li>请求与保持条件：一个进程因请求资源而阻塞时，对已获得的资源保持不放。</li>
<li>不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺，只有自己使用完毕后才释放资源。</li>
<li>循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。</li>
</ul>
<p><strong>如何查看线程死锁：</strong>通过<code>jstack命令</code>进行查看，jstack中会显示发生死锁的线程</p>
<blockquote>
<p>数据库中查看死锁：<br>查看是否有表锁：show OPEN TABLES where In_use &gt; 0；<br>查询进程：show processlist;<br>查看正在锁的事务：SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;<br>查看等待锁的事务：SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS_WAITS;</p>
</blockquote>
<p><strong>如何避免线程死锁?</strong></p>
<p>只要破坏产生死锁的四个条件中的其中一个就可以了</p>
<ul>
<li>破坏互斥条件 这个条件我们没有办法破坏，因为我们用锁本来就是想让他们互斥的（临界资源需要互斥访问）</li>
<li>破坏请求与保持条件 一次性申请所有的资源。</li>
<li>破坏不剥夺条件 占用部分资源的线程进一步申请其他资源时，如果申请不到，可以主动释放它占有的资源。</li>
<li>破坏循环等待条件 靠按序申请资源来预防。按某一顺序申请资源，释放资源则反序释放。破坏循环等待条件。</li>
<li>锁排序法：（必须回答出来的点） 指定获取锁的顺序，比如某个线程只有获得A锁和B锁，才能对某资源进行操作，在多线程条件下，如何避免死锁？ 通过指定锁的获取顺序，比如规定，只有获得A锁的线程才有资格获取B锁，按顺序获取锁就可以避免死锁。这通常被认为是解决死锁很好的一种方法。</li>
<li>使用显式锁中的ReentrantLock.try(long,TimeUnit)来申请锁</li>
</ul>
<h3 id="16、Unsafe类"><a href="#16、Unsafe类" class="headerlink" title="16、Unsafe类"></a>16、Unsafe类</h3><p>Unsafe是位于sun.misc包下的一个类，主要提供一些用于执行低级别、不安全操作的方法，如直接访问系统内存资源、自主管理内存资源等，这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力，这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大，使得Java这种安全的语言变得不再“安全”，因此对Unsafe的使用一定要慎重。</p>
<p>这个类尽管里面的方法都是 public 的，但是并没有办法使用它们，JDK API 文档也没有提供任何关于这个类的方法的解释。总而言之，对于 Unsafe 类的使用都是受限制的，只有授信的代码才能获得该类的实例，当然 JDK 库里面的类是可以随意使用的。</p>
<p>先来看下这张图，对UnSafe类总体功能：</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906203640577.png" alt="image-20220906203640577"></p>
<p>如上图所示，Unsafe提供的API大致可分为内存操作、CAS、Class相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等几类，下面将对其相关方法和应用场景进行详细介绍。</p>
<h4 id="Unsafe与CAS"><a href="#Unsafe与CAS" class="headerlink" title="Unsafe与CAS"></a>Unsafe与CAS</h4><p>反编译出来的代码：</p>
<pre class=" language-java"><code class="language-java">著作权归https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>pdai<span class="token punctuation">.</span>tech所有。
链接：https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>pdai<span class="token punctuation">.</span>tech<span class="token operator">/</span>md<span class="token operator">/</span>java<span class="token operator">/</span>thread<span class="token operator">/</span>java<span class="token operator">-</span>thread<span class="token operator">-</span>x<span class="token operator">-</span>juc<span class="token operator">-</span>AtomicInteger<span class="token punctuation">.</span>html

<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndAddInt</span><span class="token punctuation">(</span>Object paramObject<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong<span class="token punctuation">,</span> <span class="token keyword">int</span> paramInt<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token keyword">int</span> i<span class="token punctuation">;</span>
    <span class="token keyword">do</span>
      i <span class="token operator">=</span> <span class="token function">getIntVolatile</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">compareAndSwapInt</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong<span class="token punctuation">,</span> i<span class="token punctuation">,</span> i <span class="token operator">+</span> paramInt<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> i<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">long</span> <span class="token function">getAndAddLong</span><span class="token punctuation">(</span>Object paramObject<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong1<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong2<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token keyword">long</span> l<span class="token punctuation">;</span>
    <span class="token keyword">do</span>
      l <span class="token operator">=</span> <span class="token function">getLongVolatile</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong1<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">compareAndSwapLong</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong1<span class="token punctuation">,</span> l<span class="token punctuation">,</span> l <span class="token operator">+</span> paramLong2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> l<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndSetInt</span><span class="token punctuation">(</span>Object paramObject<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong<span class="token punctuation">,</span> <span class="token keyword">int</span> paramInt<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token keyword">int</span> i<span class="token punctuation">;</span>
    <span class="token keyword">do</span>
      i <span class="token operator">=</span> <span class="token function">getIntVolatile</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">compareAndSwapInt</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong<span class="token punctuation">,</span> i<span class="token punctuation">,</span> paramInt<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> i<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">long</span> <span class="token function">getAndSetLong</span><span class="token punctuation">(</span>Object paramObject<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong1<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong2<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token keyword">long</span> l<span class="token punctuation">;</span>
    <span class="token keyword">do</span>
      l <span class="token operator">=</span> <span class="token function">getLongVolatile</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong1<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">compareAndSwapLong</span><span class="token punctuation">(</span>paramObject<span class="token punctuation">,</span> paramLong1<span class="token punctuation">,</span> l<span class="token punctuation">,</span> paramLong2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> l<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">public</span> <span class="token keyword">final</span> Object <span class="token function">getAndSetObject</span><span class="token punctuation">(</span>Object paramObject1<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong<span class="token punctuation">,</span> Object paramObject2<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    Object localObject<span class="token punctuation">;</span>
    <span class="token keyword">do</span>
      localObject <span class="token operator">=</span> <span class="token function">getObjectVolatile</span><span class="token punctuation">(</span>paramObject1<span class="token punctuation">,</span> paramLong<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">compareAndSwapObject</span><span class="token punctuation">(</span>paramObject1<span class="token punctuation">,</span> paramLong<span class="token punctuation">,</span> localObject<span class="token punctuation">,</span> paramObject2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> localObject<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</code></pre>
<p>从源码中发现，内部使用自旋的方式进行CAS更新(while循环进行CAS更新，如果更新失败，则循环再次重试)。</p>
<p>又从Unsafe类中发现，原子操作其实只支持下面三个方法。</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">native</span> <span class="token keyword">boolean</span> <span class="token function">compareAndSwapObject</span><span class="token punctuation">(</span>Object paramObject1<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong<span class="token punctuation">,</span> Object paramObject2<span class="token punctuation">,</span> Object paramObject3<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">native</span> <span class="token keyword">boolean</span> <span class="token function">compareAndSwapInt</span><span class="token punctuation">(</span>Object paramObject<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong<span class="token punctuation">,</span> <span class="token keyword">int</span> paramInt1<span class="token punctuation">,</span> <span class="token keyword">int</span> paramInt2<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">native</span> <span class="token keyword">boolean</span> <span class="token function">compareAndSwapLong</span><span class="token punctuation">(</span>Object paramObject<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong1<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong2<span class="token punctuation">,</span> <span class="token keyword">long</span> paramLong3<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>我们发现Unsafe只提供了3种CAS方法：compareAndSwapObject、compareAndSwapInt和compareAndSwapLong。都是native方法。</p>
<h4 id="AtomicInteger"><a href="#AtomicInteger" class="headerlink" title="AtomicInteger"></a>AtomicInteger</h4><p>使用举例：以 AtomicInteger 为例，常用 API：</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span>：获取当前的值
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndSet</span><span class="token punctuation">(</span><span class="token keyword">int</span> newValue<span class="token punctuation">)</span>：获取当前的值，并设置新的值
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndIncrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span>：获取当前的值，并自增
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndDecrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span>：获取当前的值，并自减
<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndAdd</span><span class="token punctuation">(</span><span class="token keyword">int</span> delta<span class="token punctuation">)</span>：获取当前的值，并加上预期的值
<span class="token keyword">void</span> <span class="token function">lazySet</span><span class="token punctuation">(</span><span class="token keyword">int</span> newValue<span class="token punctuation">)</span><span class="token operator">:</span> 最终会设置成newValue<span class="token punctuation">,</span>使用lazySet设置值后，可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
</code></pre>
<p>相比 Integer 的优势，多线程中让变量自增：</p>
<pre class=" language-java"><code class="language-java">著作权归https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>pdai<span class="token punctuation">.</span>tech所有。
链接：https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>pdai<span class="token punctuation">.</span>tech<span class="token operator">/</span>md<span class="token operator">/</span>java<span class="token operator">/</span>thread<span class="token operator">/</span>java<span class="token operator">-</span>thread<span class="token operator">-</span>x<span class="token operator">-</span>juc<span class="token operator">-</span>AtomicInteger<span class="token punctuation">.</span>html

<span class="token keyword">private</span> <span class="token keyword">volatile</span> <span class="token keyword">int</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">// 若要线程安全执行执行 count++，需要加锁</span>
<span class="token keyword">public</span> <span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    count<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">getCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> count<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>使用 AtomicInteger 后：</p>
<pre class=" language-java"><code class="language-java">著作权归https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>pdai<span class="token punctuation">.</span>tech所有。
链接：https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>pdai<span class="token punctuation">.</span>tech<span class="token operator">/</span>md<span class="token operator">/</span>java<span class="token operator">/</span>thread<span class="token operator">/</span>java<span class="token operator">-</span>thread<span class="token operator">-</span>x<span class="token operator">-</span>juc<span class="token operator">-</span>AtomicInteger<span class="token punctuation">.</span>html

<span class="token keyword">private</span> AtomicInteger count <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AtomicInteger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    count<span class="token punctuation">.</span><span class="token function">incrementAndGet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">// 使用 AtomicInteger 后，不需要加锁，也可以实现线程安全</span>
<span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">getCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> count<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  
</code></pre>
<p>源码解析</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AtomicInteger</span> <span class="token keyword">extends</span> <span class="token class-name">Number</span> <span class="token keyword">implements</span> <span class="token class-name">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>Serializable</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Unsafe unsafe <span class="token operator">=</span> Unsafe<span class="token punctuation">.</span><span class="token function">getUnsafe</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> valueOffset<span class="token punctuation">;</span>
    <span class="token keyword">static</span> <span class="token punctuation">{</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token comment" spellcheck="true">//用于获取value字段相对当前对象的“起始地址”的偏移量</span>
            valueOffset <span class="token operator">=</span> unsafe<span class="token punctuation">.</span><span class="token function">objectFieldOffset</span><span class="token punctuation">(</span>AtomicInteger<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getDeclaredField</span><span class="token punctuation">(</span><span class="token string">"value"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">volatile</span> <span class="token keyword">int</span> value<span class="token punctuation">;</span>

    <span class="token comment" spellcheck="true">//返回当前值</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment" spellcheck="true">//递增加detla</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">getAndAdd</span><span class="token punctuation">(</span><span class="token keyword">int</span> delta<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment" spellcheck="true">//三个参数，1、当前的实例 2、value实例变量的偏移量 3、当前value要加上的数(value+delta)。</span>
        <span class="token keyword">return</span> unsafe<span class="token punctuation">.</span><span class="token function">getAndAddInt</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> valueOffset<span class="token punctuation">,</span> delta<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment" spellcheck="true">//递增加1</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">incrementAndGet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> unsafe<span class="token punctuation">.</span><span class="token function">getAndAddInt</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> valueOffset<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre>
<p>我们可以看到 AtomicInteger 底层用的是volatile的变量和CAS来进行更改数据的。</p>
<ul>
<li>volatile保证线程的可见性，多线程并发时，一个线程修改数据，可以保证其它线程立马看到修改后的值</li>
<li>CAS 保证数据更新的原子性</li>
</ul>
<h3 id="17、LockSupport"><a href="#17、LockSupport" class="headerlink" title="17、LockSupport"></a>17、LockSupport</h3><p>LockSupport用来创建锁和其他同步类的基本线程阻塞原语。简而言之，当调用LockSupport.park时，表示当前线程将会等待，直至获得许可，当调用LockSupport.unpark时，必须把等待获得许可的线程作为参数进行传递，好让此线程继续运行</p>
<h4 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h4><pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">LockSupport</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// Hotspot implementation via intrinsics API</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> sun<span class="token punctuation">.</span>misc<span class="token punctuation">.</span>Unsafe UNSAFE<span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 表示内存偏移地址</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> parkBlockerOffset<span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 表示内存偏移地址</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> SEED<span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 表示内存偏移地址</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> PROBE<span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 表示内存偏移地址</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> SECONDARY<span class="token punctuation">;</span>
    
    <span class="token keyword">static</span> <span class="token punctuation">{</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token comment" spellcheck="true">// 获取Unsafe实例</span>
            UNSAFE <span class="token operator">=</span> sun<span class="token punctuation">.</span>misc<span class="token punctuation">.</span>Unsafe<span class="token punctuation">.</span><span class="token function">getUnsafe</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment" spellcheck="true">// 线程类类型</span>
            Class<span class="token operator">&lt;</span><span class="token operator">?</span><span class="token operator">></span> tk <span class="token operator">=</span> Thread<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">;</span>
            <span class="token comment" spellcheck="true">// 获取Thread的parkBlocker字段的内存偏移地址</span>
            parkBlockerOffset <span class="token operator">=</span> UNSAFE<span class="token punctuation">.</span><span class="token function">objectFieldOffset</span>
                <span class="token punctuation">(</span>tk<span class="token punctuation">.</span><span class="token function">getDeclaredField</span><span class="token punctuation">(</span><span class="token string">"parkBlocker"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment" spellcheck="true">// 获取Thread的threadLocalRandomSeed字段的内存偏移地址</span>
            SEED <span class="token operator">=</span> UNSAFE<span class="token punctuation">.</span><span class="token function">objectFieldOffset</span>
                <span class="token punctuation">(</span>tk<span class="token punctuation">.</span><span class="token function">getDeclaredField</span><span class="token punctuation">(</span><span class="token string">"threadLocalRandomSeed"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment" spellcheck="true">// 获取Thread的threadLocalRandomProbe字段的内存偏移地址</span>
            PROBE <span class="token operator">=</span> UNSAFE<span class="token punctuation">.</span><span class="token function">objectFieldOffset</span>
                <span class="token punctuation">(</span>tk<span class="token punctuation">.</span><span class="token function">getDeclaredField</span><span class="token punctuation">(</span><span class="token string">"threadLocalRandomProbe"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment" spellcheck="true">// 获取Thread的threadLocalRandomSecondarySeed字段的内存偏移地址</span>
            SECONDARY <span class="token operator">=</span> UNSAFE<span class="token punctuation">.</span><span class="token function">objectFieldOffset</span>
                <span class="token punctuation">(</span>tk<span class="token punctuation">.</span><span class="token function">getDeclaredField</span><span class="token punctuation">(</span><span class="token string">"threadLocalRandomSecondarySeed"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<h4 id="类的构造函数"><a href="#类的构造函数" class="headerlink" title="类的构造函数"></a><strong>类的构造函数</strong></h4><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">// 私有构造函数，无法被实例化</span>
<span class="token keyword">private</span> <span class="token function">LockSupport</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre>
<p>说明: LockSupport只有一个私有构造函数，无法被实例化</p>
<h4 id="核心函数分析"><a href="#核心函数分析" class="headerlink" title="核心函数分析"></a>核心函数分析</h4><p>在分析LockSupport函数之前，先引入sun.misc.Unsafe类中的park和unpark函数，因为LockSupport的核心函数都是基于Unsafe类中定义的park和unpark函数，下面给出两个函数的定义:</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">native</span> <span class="token keyword">void</span> <span class="token function">park</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> isAbsolute<span class="token punctuation">,</span> <span class="token keyword">long</span> time<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">native</span> <span class="token keyword">void</span> <span class="token function">unpark</span><span class="token punctuation">(</span>Thread thread<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>说明: 对两个函数的说明如下:</p>
<ul>
<li>park函数，阻塞线程，并且该线程在下列情况发生之前都会被阻塞: ① 调用unpark函数，释放该线程的许可。② 该线程被中断。③ 设置的时间到了。并且，当time为绝对时间时，isAbsolute为true，否则，isAbsolute为false。当time为0时，表示无限等待，直到unpark发生。</li>
<li>unpark函数，释放线程的许可，即激活调用park后阻塞的线程。这个函数不是安全的，调用这个函数时要确保线程依旧存活。</li>
</ul>
<h5 id="park函数"><a href="#park函数" class="headerlink" title="park函数"></a>park函数</h5><p>park函数有两个重载版本，方法摘要如下</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">park</span><span class="token punctuation">(</span><span class="token punctuation">)</span>；
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">park</span><span class="token punctuation">(</span>Object blocker<span class="token punctuation">)</span>；
</code></pre>
<p>说明: 两个函数的区别在于park()函数没有没有blocker，即没有设置线程的parkBlocker字段。park(Object)型函数如下。</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">park</span><span class="token punctuation">(</span>Object blocker<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 获取当前线程</span>
    Thread t <span class="token operator">=</span> Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 设置Blocker</span>
    <span class="token function">setBlocker</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> blocker<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 获取许可</span>
    UNSAFE<span class="token punctuation">.</span><span class="token function">park</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">,</span> 0L<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 重新可运行后再此设置Blocker</span>
    <span class="token function">setBlocker</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
      
</code></pre>
<p>说明: 调用park函数时，首先获取当前线程，然后设置当前线程的parkBlocker字段，即调用setBlocker函数，之后调用Unsafe类的park函数，之后再调用setBlocker函数。那么问题来了，为什么要在此park函数中要调用两次setBlocker函数呢? 原因其实很简单，调用park函数时，当前线程首先设置好parkBlocker字段，然后再调用Unsafe的park函数，此后，当前线程就已经阻塞了，等待该线程的unpark函数被调用，所以后面的一个setBlocker函数无法运行，unpark函数被调用，该线程获得许可后，就可以继续运行了，也就运行第二个setBlocker，把该线程的parkBlocker字段设置为null，这样就完成了整个park函数的逻辑。如果没有第二个setBlocker，那么之后没有调用park(Object blocker)，而直接调用getBlocker函数，得到的还是前一个park(Object blocker)设置的blocker，显然是不符合逻辑的。总之，必须要保证在park(Object blocker)整个函数执行完后，该线程的parkBlocker字段又恢复为null。所以，park(Object)型函数里必须要调用setBlocker函数两次。setBlocker方法如下。</p>
<p>著作权归<a target="_blank" rel="noopener" href="https://pdai.tech所有./">https://pdai.tech所有。</a> 链接：<a target="_blank" rel="noopener" href="https://pdai.tech/md/java/thread/java-thread-x-lock-LockSupport.html">https://pdai.tech/md/java/thread/java-thread-x-lock-LockSupport.html</a></p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">setBlocker</span><span class="token punctuation">(</span>Thread t<span class="token punctuation">,</span> Object arg<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 设置线程t的parkBlocker字段的值为arg</span>
    UNSAFE<span class="token punctuation">.</span><span class="token function">putObject</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> parkBlockerOffset<span class="token punctuation">,</span> arg<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  
        <span class="token annotation punctuation">@pdai</span><span class="token operator">:</span> 代码已经复制到剪贴板
    
</code></pre>
<p>说明: 此方法用于设置线程t的parkBlocker字段的值为arg。</p>
<p>另外一个无参重载版本，park()函数如下。</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">park</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 获取许可，设置时间为无限长，直到可以获取许可</span>
    UNSAFE<span class="token punctuation">.</span><span class="token function">park</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">,</span> 0L<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  
        <span class="token annotation punctuation">@pdai</span><span class="token operator">:</span> 代码已经复制到剪贴板
    
</code></pre>
<p>说明: 调用了park函数后，会禁用当前线程，除非许可可用。在以下三种情况之一发生之前，当前线程都将处于休眠状态，即下列情况发生时，当前线程会获取许可，可以继续运行。</p>
<ul>
<li>其他某个线程将当前线程作为目标调用 unpark。</li>
<li>其他某个线程中断当前线程。</li>
<li>该调用不合逻辑地(即毫无理由地)返回</li>
</ul>
<h4 id="parkNanos函数"><a href="#parkNanos函数" class="headerlink" title="parkNanos函数"></a>parkNanos函数</h4><p>此函数表示在许可可用前禁用当前线程，并最多等待指定的等待时间。具体函数如下。</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">parkNanos</span><span class="token punctuation">(</span>Object blocker<span class="token punctuation">,</span> <span class="token keyword">long</span> nanos<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>nanos <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 时间大于0</span>
        <span class="token comment" spellcheck="true">// 获取当前线程</span>
        Thread t <span class="token operator">=</span> Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment" spellcheck="true">// 设置Blocker</span>
        <span class="token function">setBlocker</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> blocker<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment" spellcheck="true">// 获取许可，并设置了时间</span>
        UNSAFE<span class="token punctuation">.</span><span class="token function">park</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">,</span> nanos<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment" spellcheck="true">// 设置许可</span>
        <span class="token function">setBlocker</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  
        <span class="token annotation punctuation">@pdai</span><span class="token operator">:</span> 代码已经复制到剪贴板
    
</code></pre>
<p>说明: 该函数也是调用了两次setBlocker函数，nanos参数表示相对时间，表示等待多长时间。</p>
<h4 id="parkUntil函数"><a href="#parkUntil函数" class="headerlink" title="parkUntil函数"></a>parkUntil函数</h4><p>此函数表示在指定的时限前禁用当前线程，除非许可可用, 具体函数如下:</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">parkUntil</span><span class="token punctuation">(</span>Object blocker<span class="token punctuation">,</span> <span class="token keyword">long</span> deadline<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 获取当前线程</span>
    Thread t <span class="token operator">=</span> Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 设置Blocker</span>
    <span class="token function">setBlocker</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> blocker<span class="token punctuation">)</span><span class="token punctuation">;</span>
    UNSAFE<span class="token punctuation">.</span><span class="token function">park</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">,</span> deadline<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment" spellcheck="true">// 设置Blocker为null</span>
    <span class="token function">setBlocker</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
  
        <span class="token annotation punctuation">@pdai</span><span class="token operator">:</span> 代码已经复制到剪贴板
    
</code></pre>
<p>说明: 该函数也调用了两次setBlocker函数，deadline参数表示绝对时间，表示指定的时间。</p>
<p>unpark函数</p>
<p>此函数表示如果给定线程的许可尚不可用，则使其可用。如果线程在 park 上受阻塞，则它将解除其阻塞状态。否则，保证下一次调用 park 不会受阻塞。如果给定线程尚未启动，则无法保证此操作有任何效果。具体函数如下:</p>
<pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">unpark</span><span class="token punctuation">(</span>Thread thread<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>thread <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 线程为不空</span>
        UNSAFE<span class="token punctuation">.</span><span class="token function">unpark</span><span class="token punctuation">(</span>thread<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 释放该线程许可</span>
<span class="token punctuation">}</span>
  
        <span class="token annotation punctuation">@pdai</span><span class="token operator">:</span> 代码已经复制到剪贴板
    
</code></pre>
<p>说明: 释放许可，指定线程可以继续运行</p>
<h3 id="18、AQS相关"><a href="#18、AQS相关" class="headerlink" title="18、AQS相关"></a>18、AQS相关</h3><h4 id="说一说什么是AQS？"><a href="#说一说什么是AQS？" class="headerlink" title="说一说什么是AQS？"></a>说一说什么是AQS？</h4><ol>
<li>AQS 是一个锁框架，它定义了锁的实现机制，并开放出扩展的地方，让子类去实现，比如我们在 lock 的时候，AQS 开放出 state 字段，让子类可以根据 state 字段来决定是否能够获得锁，对于获取不到锁的线程 AQS 会自动进行管理，无需子类锁关心，这就是 lock 时锁的内部机制，封装的很好，又暴露出子类锁需要扩展的地方；</li>
<li>AQS 底层是由FIFO同步队列 + 条件队列联手组成，同步队列管理着获取不到锁的线程的排队和释放，条件队列是在一定场景下，对同步队列的补充，比如获得锁的线程从空队列中拿数据，肯定是拿不到数据的，这时候条件队列就会管理该线程，使该线程阻塞；</li>
<li>AQS 围绕两个队列，提供了四大场景，分别是：获得锁、释放锁、条件队列的阻塞，条件队列的唤醒，分别对应着 AQS 架构图中的四种颜色的线的走向。</li>
</ol>
<blockquote>
<p>抢到资源的线程直接使用处理业务，抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待（类似银行业务办理窗口都满了，暂时没有受理窗口的顾客只能去候客区排队等候），但等候线程任然保留获取锁的可能且获取锁流程仍在继续（候客区的顾客也在等着叫号，轮到了再去受理窗口办理业务）。</p>
</blockquote>
<h4 id="AQS使用了哪些设计模式？"><a href="#AQS使用了哪些设计模式？" class="headerlink" title="AQS使用了哪些设计模式？"></a>AQS使用了哪些设计模式？</h4><p>AQS同步器的设计是基于模板方法模式的，如果需要自定义同步器一般的方式是这样（模板方法模式很经典的一个应用）：</p>
<ol>
<li>使用者继承AbstractQueuedSynchronizer并重写指定的方法。（这些重写方法很简单，无非是对于共享资源state的获取和释放）</li>
<li>将AQS组合在自定义同步组件的实现中，并调用其模板方法，而这些模板方法会调用使用者重写的方法。</li>
</ol>
<p>这和我们以往通过实现接口的方式有很大区别，这是模板方法模式很经典的一个运用。</p>
<p><strong>AQS使用了模板方法模式，自定义同步器时需要重写下面几个AQS提供的模板方法：</strong></p>
<pre><code>isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源，成功则返回true，失败则返回false。
tryRelease(int)//独占方式。尝试释放资源，成功则返回true，失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败；0表示成功，但没有剩余可用资源；正数表示成功，且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源，成功则返回true，失败则返回false。
</code></pre>
<h4 id="了解AQS中同步队列的数据结构吗？"><a href="#了解AQS中同步队列的数据结构吗？" class="headerlink" title="了解AQS中同步队列的数据结构吗？"></a>了解AQS中同步队列的数据结构吗？</h4><p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20210822170028290.png" alt="image-20210822170028290"></p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220908172457295.png" alt="image-20220908172457295"></p>
<p>如果共享资源被占用，就需要一定的阻塞时间等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的，将暂时获取不到锁的线程加入到队列中，这个队列就是AQS同步队列的抽象表现，他要将请求共享资源的线程及自身的等待状态封装成队列的结点对象（Node），通过CAS、自旋以及LockSupport.park()的方式，维护state变量的状态。使并发达到同步的效果。</p>
<ul>
<li>当前线程获取同步状态失败，同步器将当前线程机等待状态等信息构造成一个Node节点加入队列，放在队尾，同步器重新设置尾节点</li>
<li>加入队列后，会阻塞当前线程</li>
<li>同步状态被释放并且同步器重新设置首节点，同步器唤醒等待队列中第一个节点，让其再次获取同步状态</li>
</ul>
<h4 id="4-了解AQS-对资源的共享方式吗？"><a href="#4-了解AQS-对资源的共享方式吗？" class="headerlink" title="4. 了解AQS 对资源的共享方式吗？"></a>4. 了解AQS 对资源的共享方式吗？</h4><p><strong>AQS定义两种资源共享方式</strong></p>
<ul>
<li><p>Exclusive</p>
<p>（独占）：只有一个线程能执行，如ReentrantLock。又可分为公平锁和非公平锁：</p>
<ul>
<li>公平锁：按照线程在队列中的排队顺序，先到者先拿到锁</li>
<li>非公平锁：当线程要获取锁时，无视队列顺序直接去抢锁，谁抢到就是谁的</li>
</ul>
</li>
<li><p><strong>Share</strong>（共享）：多个线程可同时执行，如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。</p>
</li>
</ul>
<p>ReentrantReadWriteLock 可以看成是组合式，因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。</p>
<p>不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可，至于具体线程等待队列的维护（如获取资源失败入队/唤醒出队等），AQS已经在顶层实现好了。</p>
<h4 id="AQS-组件了解吗"><a href="#AQS-组件了解吗" class="headerlink" title="AQS 组件了解吗?"></a>AQS 组件了解吗?</h4><ul>
<li><strong>Semaphore(信号量)-允许多个线程同时访问：</strong> synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源，Semaphore(信号量)可以指定多个线程同时访问某个资源。</li>
<li><strong>CountDownLatch （倒计时器）：</strong> CountDownLatch是一个同步工具类，用来协调多个线程之间的同步。这个工具通常用来控制线程等待，它可以让某一个线程等待直到倒计时结束，再开始执行。</li>
<li><strong>CyclicBarrier(循环栅栏)：</strong> CyclicBarrier 和 CountDownLatch 非常类似，它也可以实现线程间的技术等待，但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。CyclicBarrier 的字面意思是可循环使用（Cyclic）的屏障（Barrier）。它要做的事情是，让一组线程到达一个屏障（也可以叫同步点）时被阻塞，直到最后一个线程到达屏障时，屏障才会开门，所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是 CyclicBarrier(int parties)，其参数表示屏障拦截的线程数量，每个线程调用await方法告诉 CyclicBarrier 我已经到达了屏障，然后当前线程被阻塞。</li>
</ul>
<h3 id="19、Atomic原子类"><a href="#19、Atomic原子类" class="headerlink" title="19、Atomic原子类"></a>19、Atomic原子类</h3><h4 id="介绍一下-Atomic-原子类"><a href="#介绍一下-Atomic-原子类" class="headerlink" title="介绍一下 Atomic 原子类"></a>介绍一下 Atomic 原子类</h4><p>Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候，一个操作一旦开始，就不会被其他线程干扰。</p>
<p>所以，所谓原子类说简单点就是具有原子 / 原子操作特征的类。</p>
<p>并发包 java.util.concurrent 的原子类都存放在 java.util.concurrent.atomic 下：</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/atomic1.png" alt="atomic"></p>
<h4 id="JUC-包中的原子类是哪4类？"><a href="#JUC-包中的原子类是哪4类？" class="headerlink" title="JUC 包中的原子类是哪4类？"></a>JUC 包中的原子类是哪4类？</h4><p><strong>基本类型</strong> 使用原子的方式更新基本类型：</p>
<ul>
<li>AtomicInteger ： 整型原子类</li>
<li>AtomicLong： 长整型原子类</li>
<li>AtomicBoolean： 布尔型原子类</li>
</ul>
<p><strong>数组类型</strong> 使用原子的方式更新数组里的某个元素：</p>
<ul>
<li>AtomicIntegerArray： 整型数组原子类</li>
<li>AtomicLongArray： 长整型数组原子类</li>
<li>AtomicReferenceArray： 引用类型数组原子类</li>
</ul>
<p><strong>引用类型</strong> 使用原子的方式更新引用类型：</p>
<ul>
<li>AtomicReference： 引用类型原子类</li>
<li>AtomicStampedReference： 原子更新带有版本号的引用类型。该类将整型数值与引用关联起来，可用于解决原子的更新数据和数据的版本号，可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。</li>
<li>AtomicMarkableReference： 原子更新带有标记位的引用类型。<strong>对象属性修改类型</strong></li>
<li>AtomicIntegerFieldUpdater： 原子更新整型字段的更新器</li>
<li>AtomicLongFieldUpdater： 原子更新长整型字段的更新器</li>
<li>AtomicMarkableReference： 原子更新带有标记位的引用类型</li>
</ul>
<h4 id="简单介绍一下-AtomicInteger-类的原理"><a href="#简单介绍一下-AtomicInteger-类的原理" class="headerlink" title="简单介绍一下 AtomicInteger 类的原理"></a>简单介绍一下 AtomicInteger 类的原理</h4><p>AtomicInteger 类主要利用 CAS和 volatile 和 native 方法来保证原子操作，从而避免 synchronized 的高开销，执行效率大为提升。</p>
<p><code>AtomicInteger</code> 类的部分源码：</p>
<pre class=" language-JAVA"><code class="language-JAVA">  // 更新操作时提供“比较并替换”的作用
  private static final Unsafe unsafe = Unsafe.getUnsafe();

  private static final long valueOffset;

  static {
      try{
          valueOffset = unsafe.objectFieldOffset(AutomicInteger.class.getDeclaredField("value"));
      }catch(Exception ex){
          throw new Error(ex);
      }
  }

  private volatile int value;
</code></pre>
<h3 id="20、ReentrantLock"><a href="#20、ReentrantLock" class="headerlink" title="20、ReentrantLock"></a>20、ReentrantLock</h3><p>ReetrantLock是一个可重入的独占锁，主要有两个特性，一个是支持公平锁和非公平锁，一个是可重入。 ReetrantLock实现依赖于AQS(AbstractQueuedSynchronizer)。</p>
<p>ReetrantLock主要依靠AQS维护一个阻塞队列，多个线程对加锁时，失败则会进入阻塞队列。等待唤醒，重新尝试加锁。</p>
<h2 id="三、JMM"><a href="#三、JMM" class="headerlink" title="三、JMM"></a>三、JMM</h2><h3 id="1、什么是JMM？"><a href="#1、什么是JMM？" class="headerlink" title="1、什么是JMM？"></a>1、什么是JMM？</h3><p>内存模型可以理解为在特定的操作协议下，对特定的内存或者高速缓存进行读写访问的过程抽象描述，不同架构下的物理机拥有不一样的内存模型，Java虚拟机是一个实现了跨平台的虚拟系统，因此它也有自己的内存模型，JMM即Java内存模型（Java Memory Model,）。<br>因此它不是对物理内存的规范，而是在虚拟机基础上进行的规范从而实现平台一致性，以达到Java程序能够“一次编写，到处运行“。</p>
<p>从抽象的角度来看，JMM定义了线程和主内存之间的抽象关系：线程之间的共享变量存储在主内存（main memory）中，每个线程都有一个私有的本地内存（local memory），本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念，并不真实存在，它涵盖了缓存，写缓冲区，寄存器以及其他的硬件和编译器优化。Java内存模型的抽象示意图如下：</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906164355968.png" alt="image-20220906164355968"></p>
<h3 id="2、内存模型是什么"><a href="#2、内存模型是什么" class="headerlink" title="2、内存模型是什么"></a>2、内存模型是什么</h3><p>内存模型描述了程序中各个变量（实例域、静态域和数组元素）之间的关系，以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节</p>
<h3 id="3、为什么要有内存模型"><a href="#3、为什么要有内存模型" class="headerlink" title="3、为什么要有内存模型"></a>3、为什么要有内存模型</h3><p>在传统计算机硬件内存架构中由于CPU 和主内存间存在数量级的速率差，想到了引入了多级高速缓存的传统硬件内存架构来解决，多级高速缓存作为 CPU 和主内间的缓冲提升了整体性能。解决了速率差的问题，却又带来了缓存一致性问题。数据同时存在于高速缓存和主内存中，如果不加以规范势必造成灾难，因此在传统机器上又抽象出了内存模型<br><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906164501745.png" alt="image-20220906164501745"></p>
<h3 id="4、JMM内存模型"><a href="#4、JMM内存模型" class="headerlink" title="4、JMM内存模型"></a>4、JMM内存模型</h3><p>Java 语言在遵循内存模型的基础上推出了 JMM 规范，目的是解决由于多线程通过共享内存进行通信时，存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题，因此JMM的关键技术都是围绕多线程的<strong>原子性、可见性和有序性</strong>展开的。<br>为了更精准控制工作内存和主内存间的交互，JMM 还定义了八种操作：lock, unlock, read, load, use, assign, store, write。</p>
<ul>
<li>lock（锁定）：作用于主内存的变量，把一个变量标识为一条线程独占的状态。</li>
<li>unclock（解锁）：作用于主内存的变量，把一个处于锁定的状态释放出来。</li>
<li>read（读取）：作用于主内存的变量，把一个变量的值从主内存传输到线程的工作内存中</li>
<li>load（载入）：作用于工作内存的变量，把read操作从主内存 得到的变量值放入工作内存的变量副本中。</li>
<li>use（使用）：作用于工作内存的变量，把工作内存中一个变量的值传递给执行引擎，每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。</li>
<li>assign（赋值）：作用于工作内存的变量，把一个从执行引擎接收到的值 赋值给工作内存的变量，每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。</li>
<li>store（存储）：作用于工作内存的变量，把工作内存中的一个变量的值传递到主内存，以便write操作使用。</li>
<li>write（写入）：作用于主内存的变量，把store操作从工作内存中得到的变量的值放入主内存的变量中。</li>
</ul>
<h3 id="4、JMM三大特性"><a href="#4、JMM三大特性" class="headerlink" title="4、JMM三大特性"></a>4、JMM三大特性</h3><h4 id="可见性"><a href="#可见性" class="headerlink" title="可见性"></a>可见性</h4><p>是指当一个线程修改了某一个共享变量的值，其他线程是否能够立即知道该变更，JMM规定了所有的变量都存储在主内存中。</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906170146629.png" alt="image-20220906170146629"></p>
<p>系统主内存<strong>共享变量</strong>的数据修改被写入的时机是不确定的，<strong>多线程并发下很可能出现“脏读”</strong>，所以每个线程都有自己的<strong>工作内存</strong>，线程自己的工作内存中保存了该线程使用到的变量的<strong>主内存副本拷贝</strong>，线程对变量的所有操作（读取，赋值等）都必须在线程自己的工作内存中进行，而不能够直接读写内存中的变量，不同线程之间也无法直接访问对方工作内存中的变量，线程之间变量值的传递均需要通过主内存来完成。</p>
<h4 id="原子性"><a href="#原子性" class="headerlink" title="原子性"></a>原子性</h4><p>指一个操作是不可打断的，即多线程环境下，操作不能被其他线程干扰</p>
<h4 id="有序性"><a href="#有序性" class="headerlink" title="有序性"></a>有序性</h4><p>对于一个线程的执行代码而言，我们总是习惯性认为代码的执行总是从上到下，有序执行，但为了提升性能，编译器和处理器通常会<strong>对指令序列进行重新排序</strong>，Java规范规定JVM线程内部维持<strong>顺序化语义</strong>，即只要程序的最终结果与它顺序化执行的结果相等，name指令的执行顺序可以与代码顺序不一致，此过程叫指令的重排序。</p>
<p>优缺点：</p>
<p>JVM能根据处理器特性（CPU多级缓存系统、多核处理器等）适当的对机器指令进行重排序，使机器指令能更符合CPU的执行特性，最大限度发挥机器性能。<strong>但是</strong>指令重排可以保持串行语义一致。但没有义务保证多线程间的语义也一致（即可能产生“脏读”），简单说，两行以上不相干的代码在执行的时候有可能先执行的不是第一条，不见得是从上到下顺序执行，执行顺序会被优化</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906172826811.png" alt="image-20220906172826811"></p>
<h3 id="5、重排序"><a href="#5、重排序" class="headerlink" title="5、重排序"></a>5、重排序</h3><p>在执行程序时为了提高性能，编译器和处理器常常会对指令做重排序。重排序分三种类型：</p>
<ul>
<li><p>编译器优化的重排序。编译器在不改变单线程程序语义的前提下，可以重新安排语句的执行顺序。</p>
</li>
<li><p>指令级并行的重排序。现代处理器采用了指令级并行技术（Instruction-Level Parallelism， ILP）来将多条指令重叠执行。如果不存在数据依赖性，处理器可以改变语句对应机器指令的执行顺序。</p>
</li>
<li><p>内存系统的重排序。由于处理器使用缓存和读/写缓冲区，这使得加载和存储操作看上去可能是在乱序执行。</p>
<p>从java源代码到最终实际执行的指令序列，会分别经历下面三种重排序：</p>
<ul>
<li>上述的1属于编译器重排序，2和3属于处理器重排序。这些重排序都可能会导致多线程程序出现内存可见性问题。</li>
<li>对于编译器，JMM的编译器重排序规则会禁止特定类型的编译器重排序（不是所有的编译器重排序都要禁止）。</li>
<li>对于处理器重排序，JMM的处理器重排序规则会要求java编译器在生成指令序列时，插入特定类型的内存屏障（memory barriers，intel称之为memory fence）指令，通过内存屏障指令来禁止特定类型的处理器重排序（不是所有的处理器重排序都要禁止）。</li>
</ul>
</li>
</ul>
<h3 id="6、as-if-serial语义"><a href="#6、as-if-serial语义" class="headerlink" title="6、as-if-serial语义"></a>6、as-if-serial语义</h3><p>as-if-serial语义的意思指：不管怎么重排序（编译器和处理器为了提高并行度），（单线程）程序的执行结果不能被改变。编译器，runtime 和处理器都必须遵守as-if-serial语义。</p>
<h3 id="7、happens-before-原则"><a href="#7、happens-before-原则" class="headerlink" title="7、happens-before 原则"></a>7、happens-before 原则</h3><ul>
<li>程序次序规则：一个线程内，按照代码顺序，书写在前面的操作先行发生于书写在后面的操作</li>
<li>锁定规则：一个unLock操作先行发生于后面对同一个锁额lock操作</li>
<li>volatile变量规则：对一个变量的写操作先行发生于后面对这个变量的读操作</li>
<li>传递规则：如果操作A先行发生于操作B，而操作B又先行发生于操作C，则可以得出操作A先行发生于操作C</li>
<li>线程启动规则：Thread对象的start()方法先行发生于此线程的每个一个动作</li>
<li>线程中断规则：对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生</li>
<li>线程终结规则：线程中所有的操作都先行发生于线程的终止检测，我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行</li>
<li>对象终结规则：一个对象的初始化完成先行发生于他的finalize()方法的开始</li>
</ul>
<h3 id="8、volatile的原理和实现机制"><a href="#8、volatile的原理和实现机制" class="headerlink" title="8、volatile的原理和实现机制"></a>8、volatile的原理和实现机制</h3><p>“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现，加入volatile关键字时，会多出一个lock前缀指令”<br>lock前缀指令实际上相当于一个内存屏障（也成内存栅栏），内存屏障会提供3个功能：<br>1）它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置，也不会把前面的指令排到内存屏障的后面；即在执行到内存屏障这句指令时，在它前面的操作已经全部完成；<br>2）它会强制将对缓存的修改操作立即写入主存；<br>3）如果是写操作，它会导致其他CPU中对应的缓存行无效。</p>
<h3 id="9、Volatile特点"><a href="#9、Volatile特点" class="headerlink" title="9、Volatile特点"></a>9、Volatile特点</h3><h4 id="Volatile修饰的变量有两大特点："><a href="#Volatile修饰的变量有两大特点：" class="headerlink" title="Volatile修饰的变量有两大特点："></a>Volatile修饰的变量有两大特点：</h4><ul>
<li>可见性</li>
<li>有序性（禁止重排）</li>
</ul>
<h4 id="Volatile的内存语义"><a href="#Volatile的内存语义" class="headerlink" title="Volatile的内存语义"></a>Volatile的内存语义</h4><ul>
<li>当写一个volatile变量时，JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。</li>
<li>当读一个Volatile变量时，JMM会把该线程对应的本地内存设置为无效，重新回到主内存中读取最新共享变量</li>
<li>所以Volatile的写内存语义是直接刷新到至内存中，读的内存语义是直接从主内存中读取</li>
</ul>
<h4 id="Volatile凭什么可以保证可见性和有序性？"><a href="#Volatile凭什么可以保证可见性和有序性？" class="headerlink" title="Volatile凭什么可以保证可见性和有序性？"></a>Volatile凭什么可以保证可见性和有序性？</h4><p><strong>内存屏障</strong></p>
<p>内存屏障（也称内存栅栏，屏蔽指令等，是一类同步屏障指令，是CPU或编译器在对内存随机访问的操作中的一个同步点，使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作），避免代码重排序。内存屏障其实就是一种JVM指令，Java内存模型的重排规则会<strong>要求Java编译器在生成JVM指令时插入特定的内存屏蔽指令</strong>，通过这些内存屏蔽指令，Volatile实现了Java内存模型中的可见性和有序性（禁止重排），但Volatile<strong>无法保证原子性</strong></p>
<p>内存屏障之前的所有写操作都要回写到主内存，内存屏蔽之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果（实现了可见性）</p>
<p><strong>写屏障：</strong>告诉处理器在写屏障之前将所有存储在缓存（store bufferes）中的数据同步到主内存。也就是就当看到Store屏蔽指令，就必须把该指令之前所有写入指令执行完毕才能继续往下进行。</p>
<p><strong>读屏障：</strong>处理器在读屏障之后的读操作，都在读屏障之后执行。也就是说在Load屏蔽指令之后就能够保证后面的读取数据指令一定能够读取到最新的数据</p>
<p>因此重排序时，不容许把内存屏蔽之后的指令重排序到内存屏蔽之前。一句话：对一个Volatile变量的写，，先行发生于任意后续对这个Volatile变量的读，也叫写后读</p>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">YangChao</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://monkey-chao.gitee.io/2022/07/29/java-mian-shi-ti-bing-fa-pian/">https://monkey-chao.gitee.io/2022/07/29/java-mian-shi-ti-bing-fa-pian/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/about" target="_blank">YangChao</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/Java/">
                                    <span class="chip bg-color">Java</span>
                                </a>
                            
                                <a href="/tags/%E9%9D%A2%E8%AF%95/">
                                    <span class="chip bg-color">面试</span>
                                </a>
                            
                                <a href="/tags/%E5%B9%B6%E5%8F%91/">
                                    <span class="chip bg-color">并发</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    
    <div class="livere-card card" data-aos="fade-up">
    <!-- 来必力City版安装代码 -->
    <div id="lv-container" class="card-content" data-id="city" data-uid="">
        <script type="text/javascript">
            (function (d, s) {
                let j, e = d.getElementsByTagName(s)[0];
                if (typeof LivereTower === 'function') {
                    return;
                }

                j = d.createElement(s);
                j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
                j.async = true;

                e.parentNode.insertBefore(j, e);
            })(document, 'script');
        </script>
        <noscript>为正常使用来必力评论功能请激活JavaScript。</noscript>
    </div>
    <!-- City版安装代码已完成 -->
</div>
    

    
        <style>
    .valine-card {
        margin: 1.5rem auto;
    }

    .valine-card .card-content {
        padding: 20px 20px 5px 20px;
    }

    #vcomments textarea {
        box-sizing: border-box;
        background: url("/medias/comment_bg.png") 100% 100% no-repeat;
    }

    #vcomments p {
        margin: 2px 2px 10px;
        font-size: 1.05rem;
        line-height: 1.78rem;
    }

    #vcomments blockquote p {
        text-indent: 0.2rem;
    }

    #vcomments a {
        padding: 0 2px;
        color: #4cbf30;
        font-weight: 500;
        text-decoration: none;
    }

    #vcomments img {
        max-width: 100%;
        height: auto;
        cursor: pointer;
    }

    #vcomments ol li {
        list-style-type: decimal;
    }

    #vcomments ol,
    ul {
        display: block;
        padding-left: 2em;
        word-spacing: 0.05rem;
    }

    #vcomments ul li,
    ol li {
        display: list-item;
        line-height: 1.8rem;
        font-size: 1rem;
    }

    #vcomments ul li {
        list-style-type: disc;
    }

    #vcomments ul ul li {
        list-style-type: circle;
    }

    #vcomments table, th, td {
        padding: 12px 13px;
        border: 1px solid #dfe2e5;
    }

    #vcomments table, th, td {
        border: 0;
    }

    table tr:nth-child(2n), thead {
        background-color: #fafafa;
    }

    #vcomments table th {
        background-color: #f2f2f2;
        min-width: 80px;
    }

    #vcomments table td {
        min-width: 80px;
    }

    #vcomments h1 {
        font-size: 1.85rem;
        font-weight: bold;
        line-height: 2.2rem;
    }

    #vcomments h2 {
        font-size: 1.65rem;
        font-weight: bold;
        line-height: 1.9rem;
    }

    #vcomments h3 {
        font-size: 1.45rem;
        font-weight: bold;
        line-height: 1.7rem;
    }

    #vcomments h4 {
        font-size: 1.25rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    #vcomments h5 {
        font-size: 1.1rem;
        font-weight: bold;
        line-height: 1.4rem;
    }

    #vcomments h6 {
        font-size: 1rem;
        line-height: 1.3rem;
    }

    #vcomments p {
        font-size: 1rem;
        line-height: 1.5rem;
    }

    #vcomments hr {
        margin: 12px 0;
        border: 0;
        border-top: 1px solid #ccc;
    }

    #vcomments blockquote {
        margin: 15px 0;
        border-left: 5px solid #42b983;
        padding: 1rem 0.8rem 0.3rem 0.8rem;
        color: #666;
        background-color: rgba(66, 185, 131, .1);
    }

    #vcomments pre {
        font-family: monospace, monospace;
        padding: 1.2em;
        margin: .5em 0;
        background: #272822;
        overflow: auto;
        border-radius: 0.3em;
        tab-size: 4;
    }

    #vcomments code {
        font-family: monospace, monospace;
        padding: 1px 3px;
        font-size: 0.92rem;
        color: #e96900;
        background-color: #f8f8f8;
        border-radius: 2px;
    }

    #vcomments pre code {
        font-family: monospace, monospace;
        padding: 0;
        color: #e8eaf6;
        background-color: #272822;
    }

    #vcomments pre[class*="language-"] {
        padding: 1.2em;
        margin: .5em 0;
    }

    #vcomments code[class*="language-"],
    pre[class*="language-"] {
        color: #e8eaf6;
    }

    #vcomments [type="checkbox"]:not(:checked), [type="checkbox"]:checked {
        position: inherit;
        margin-left: -1.3rem;
        margin-right: 0.4rem;
        margin-top: -1px;
        vertical-align: middle;
        left: unset;
        visibility: visible;
    }

    #vcomments b,
    strong {
        font-weight: bold;
    }

    #vcomments dfn {
        font-style: italic;
    }

    #vcomments small {
        font-size: 85%;
    }

    #vcomments cite {
        font-style: normal;
    }

    #vcomments mark {
        background-color: #fcf8e3;
        padding: .2em;
    }

    #vcomments table, th, td {
        padding: 12px 13px;
        border: 1px solid #dfe2e5;
    }

    table tr:nth-child(2n), thead {
        background-color: #fafafa;
    }

    #vcomments table th {
        background-color: #f2f2f2;
        min-width: 80px;
    }

    #vcomments table td {
        min-width: 80px;
    }

    #vcomments [type="checkbox"]:not(:checked), [type="checkbox"]:checked {
        position: inherit;
        margin-left: -1.3rem;
        margin-right: 0.4rem;
        margin-top: -1px;
        vertical-align: middle;
        left: unset;
        visibility: visible;
    }
</style>

<div class="card valine-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; padding-left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="vcomments" class="card-content" style="display: grid">
    </div>
</div>

<script src="/libs/valine/av-min.js"></script>
<script src="/libs/valine/Valine.min.js"></script>
<script>
    new Valine({
        el: '#vcomments',
        appId: '1543373971',
        appKey: '111111',
        notify: 'false' === 'true',
        verify: 'false' === 'true',
        visitor: 'true' === 'true',
        avatar: 'mm',
        pageSize: '10',
        lang: 'zh-cn',
        placeholder: 'just go go'
    });
</script>

<!--酷Q推送-->


    

    
        <style>
    .mvaline-card {
        margin: 1.5rem auto;
    }

    .mvaline-card .card-content {
        padding: 20px 20px 5px 20px;
    }
</style>

<div class="card mvaline-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; padding-left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="mvcomments" class="card-content" style="display: grid">
    </div>
</div>

<script src="/libs/minivaline/MiniValine.js"></script>
<script>
    new MiniValine(Object.assign({"enable":true,"serverURL":"https://minivaline.your-domain.top"}, {
	  el: '#mvcomments',
    }));
</script>

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/2022/08/08/java-mian-shi-ti-kuang-jia-pian/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/1.jpg" class="responsive-img" alt="Java面试题（框架篇）">
                        
                        <span class="card-title">Java面试题（框架篇）</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            Java面试开发框架相关讲解
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2022-08-08
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E9%9D%A2%E8%AF%95%E5%AE%9D%E5%85%B8/" class="post-category">
                                    面试宝典
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Java/">
                        <span class="chip bg-color">Java</span>
                    </a>
                    
                    <a href="/tags/%E9%9D%A2%E8%AF%95/">
                        <span class="chip bg-color">面试</span>
                    </a>
                    
                    <a href="/tags/%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6/">
                        <span class="chip bg-color">开发框架</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2022/07/25/java-mian-shi-ti-ji-chu-pian/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/11.jpg" class="responsive-img" alt="Java面试题（基础篇）">
                        
                        <span class="card-title">Java面试题（基础篇）</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            Java面试题基础部分讲解
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2022-07-25
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E9%9D%A2%E8%AF%95%E5%AE%9D%E5%85%B8/" class="post-category">
                                    面试宝典
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Java/">
                        <span class="chip bg-color">Java</span>
                    </a>
                    
                    <a href="/tags/%E9%9D%A2%E8%AF%95/">
                        <span class="chip bg-color">面试</span>
                    </a>
                    
                    <a href="/tags/%E5%9F%BA%E7%A1%80/">
                        <span class="chip bg-color">基础</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('120')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE' || selection.getRangeAt(0).commonAncestorContainer.nodeName === 'CODE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + '来源: Angus<br />'
            + '文章作者: YangChao<br />'
            + '文章链接: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h2, h3, h4'
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    

    <div class="container row center-align"
         style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2019-2023</span>
            
            <a href="/about" target="_blank">YangChao</a>
<!--             |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" target="_blank">Matery</a> -->
            <br>
            
                &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                        class="white-color">460.9k</span>
            
            
            
                
            
            
                <span id="busuanzi_container_site_pv">
                &nbsp;|&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;
                    <span id="busuanzi_value_site_pv" class="white-color"></span>
            </span>
            
            
                <span id="busuanzi_container_site_uv">
                &nbsp;|&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;
                    <span id="busuanzi_value_site_uv" class="white-color"></span>
            </span>
            
            <br>

            <!-- 运行天数提醒. -->
            
            <br>
            
        </div>
<!--         <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>




<!-- 

 -->


    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1543373971" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1543373971" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>


<!-- 




    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

 --></div>
 -->     </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    

    
        
        <script type="text/javascript">
            // 只在桌面版网页启用特效
            var windowWidth = $(window).width();
            if (windowWidth > 768) {
                document.write('<script type="text/javascript" src="/libs/others/sakura.js"><\/script>');
            }
        </script>
    

    <!-- 雪花特效 -->
    

    <!-- 鼠标星星特效 -->
    

    

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    <!--腾讯兔小巢-->
    
    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
